Docker에 OpenGL 환경 설정하기까지의 과정
작업환경은 Intel Iris Plus Graphics를 탑재한 MacBook Pro 2020년형이다.
Visual Studio Code에서 작업할 것이며, Docker에 Ubuntu20.04 이미지를 사용할 것이다.
내 목표는 이 환경에서 파이썬 3.7 버전으로 OpenGL 창을 띄우는 것이다.
아래는 이를 위한 삽질을 기록하도록 한다.
도커 환경 설정
https://docs.microsoft.com/ko-kr/learn/modules/use-docker-container-dev-env-vs-code/
마이크로소프트 개발자 가이드에 친절히 나와있으므로, 이를 참고하도록 하자
파이썬 3.7 설치하기
기본적으로 파이썬 3.8 버전이 설치돼 있다. 나는 파이썬 3.7 버전이 필요한데 이를 설치하려면 다음의 방법을 참고하자.
https://askubuntu.com/questions/682869/how-do-i-install-a-different-python-version-using-apt-get
위를 따라하려고 하더라도 add-apt-repository라는 명령어를 찾을 수 없다며 첫 줄부터 막힌다. 이 때는 apt-get update를 한 번 실행해준 이후 다음을 따라하자.
https://nancom.tistory.com/119
위에서 소개한 내용 중 두 번째 해결책을 시도하자.
파이썬 기본 버전 설정하기
파이썬 3.7을 설치했다고 하더라도 python을 터미널에 입력해도 명령어를 찾을 수 없다고 뜬다.
시스템 환경변수 PATH에도 파이썬이 설치된 경로는 추가돼 있지 않다.
사실, python3를 입력하면 파이썬을 실행할 수 있지만 기본 버전이 3.8이다.
다음 명령어를 입력하여 파이썬 기본 버전을 설정할 수 있다.
sudo update-alternative /usr/bin/python python /usr/bin/python3.7 1
출처가 어디었는지는 기억나지 않는다.
아무튼, 이제 터미널에 python을 입력하면 3.7 버전이 기본적으로 실행된다.
PIP 설치하기
어째서인지 없다. 다음 글을 참고하여 설치하자
https://devlog.jwgo.kr/2020/02/29/broken-pip-error/
필요한 모듈 설치하기
glfw, pyopengl, numpy가 필요하다. pip으로 설치하고 pip show <package>를 통해 3.7 버전에 잘 설치됐는지 검토하자.
GLFW 테스트
이제 파이썬을 실행하여 glfw를 import하고 glfw.init()을 입력하면 다음과 같은 오류가 뜰거다.
/home/vscode/.local/lib/python3.7/site-packages/glfw/__init__.py:906: GLFWError: (65544) b'X11: The DISPLAY environment variable is missing' warnings.warn(message, GLFWError) 0
말그대로 DISPLAY 환경변수가 설정되지 않아서 뜨는 문제다. 다음 링크를 참고하여 따라하자.
https://hanseokhyeon.tistory.com/entry/Mac-Docker-에서-GUI-사용하기-python-matplotlib-사용하기
socat 명령어를 입력했는데 다음과 같은 오류가 떴다면 XQuartz가 켜져 있어서 그렇다. 끄자.
다른 서버가 우연히 같은 포트를 쓸 수 있지만 그건 알아서 하자.
socat[2209] E bind(5, {LEN=0 AF=2 0.0.0.0:6000}, 16): Address already in use
DISPLAY 환경변수 설정
DISPLAY 환경변수의 값은 자신의 아이피(도커 밖에서 ifconfig en0로 확인되는 아이피)에 :0을 붙인 값이다.
나의 경우 (지금 네트워크 환경에서는) 192.168.0.9:0이 그 값이 되겠다. 다음을 참고하여 설정하자. 두 번째까지 진행하자.
https://mkyong.com/linux/how-to-set-environment-variable-in-ubuntu/
필수 라이브러리 설치
위에까지 마친 이후 glfw.init()을 실행해보면 또 다른 문제가 생길 것이다. 라이브러리가 없어서 생기는 문제일 것이므로 libglfw, libglfw-dev 라이브러리를 설치해줌으로써 해결할 수 있다.
그래픽 드라이버 설치
이제 될 법도 한데, 심지어 XQuartz 테스트 프로그램도 잘 돌아가지만 OpenGL 창을 띄우려 하면 다음 오류가 뜨며 안 된다.
libGL error: No matching fbConfigs or visuals found libGL error: failed to load driver: swrast /home/vscode/.local/lib/python3.7/site-packages/glfw/__init__.py:906: GLFWError: (65543) b'GLX: Failed to create context: BadValue (integer parameter out of range for operation)' warnings.warn(message, GLFWError)
glfw.create_window 함수에서 문제가 생긴다. 인텔 내장 그래픽이므로 libgl1-mesa-glx, libgl1-mesa-dri 패키지를 설치하였으나 문제가 해결되지 않는다. 추가적으로 환경변수 LIBGL_ALWAYS_INDIRECT를 1로 설정하고 libgl1-utils, libgl1-mesa-glx 패키지도 설치했다.
X Quartz 키워드를 포함하여 검색해보니 해결책을 찾았다. 도커 외부에서 터미널을 열고 X Quartz의 설정을 바꿔줘야 한다. 아래 링크에서 제안한대로 치면 되는데 중간에 org로 시작하는 값을 다음으로 대체하면 된다.
defaults write org.xquartz.X11 enable_iglx -bool true
https://services.dartmouth.edu/TDClient/1806/Portal/KB/ArticleDet?ID=89669
이렇게 했더니 윈도우가 뜨긴 하지만, 비어있다.
X Quartz와 OpenGL 키워드로 더 검색해보니, DISPLAY 변수도 자동설정 해주는게 있는 것 같다.
GUI 프로그램 실행 테스트
다음 링크에서 소개된 GUI 프로그램들을 설치하여 실행해보았는데, 모두 잘 실행된다.
https://docs.microsoft.com/ko-kr/windows/wsl/tutorials/gui-apps
VLC에서 OpenGL로 비디오 출력 설정을 바꿔도 잘 재생된다. (연관이 얼마나 있는지는 모르겠지만)
다만, OpenGL로 개발하여 컴파일 하여 실행했을 때는 모두 내용이 그려지지 않는다.
OpenGL 샘플 코드 실행
OpenGL 홈페이지에서 샘플 코드를 다운로드 받아 실행할 수 있다.
하지만 GLFW가 아닌 GLUT를 사용하는 옛 코드다.
GLUT를 설치하여 실행했는데 잘 실행된다.
이로써 GLFW의 문제임이 유력해졌다.
보류
주말 동안, 그리고 앞으로 할 일이 많아서 이 문제는 보류해야겠다.
1. ccmake를 통해 cmake 옵션을 추가하여 빌드했다. 테스트 프로젝트들도 빌드 되는데 이것저것 실행하다보면 잘 실행되는 것도 있고 아닌 것도 있다. 그 오류를 분석하면 문제를 해결할 실마리가 보일 듯 싶다.
https://www.glfw.org/docs/latest/compile.html
2. OpenGL 설치와 관련된 설명은 아래 링크에 자세히 나와있다. 미래에 다시 이 문제를 해결할 때 참고하도록 하자.
https://medium.com/geekculture/a-beginners-guide-to-setup-opengl-in-linux-debian-2bfe02ccd1e
3. 지금 직면한 문제는 크게 세 가지다.
import glfw
from OpenGL.GL import *
import OpenGL.GL.shaders
import numpy
def main():
# initialize glfw
if not glfw.init():
return
window = glfw.create_window(800, 600, "My OpenGL window", None, None)
if not window:
glfw.terminate()
return
glfw.make_context_current(window)
# positions colors
triangle = [-0.5, -0.5, 0.0, 1.0, 0.0, 0.0,
0.5, -0.5, 0.0, 0.0, 1.0, 0.0,
0.0, 0.5, 0.0, 0.0, 0.0, 1.0]
triangle = numpy.array(triangle, dtype = numpy.float32)
vertex_shader = """
#version 330
in vec3 position;
in vec3 color;
out vec3 newColor;
void main()
{
gl_Position = vec4(position, 1.0f);
newColor = color;
}
"""
fragment_shader = """
#version 330
in vec3 newColor;
out vec4 outColor;
void main()
{
outColor = vec4(newColor, 1.0f);
}
"""
vao = glGenVertexArrays(1)
glBindVertexArray(vao)
shader = OpenGL.GL.shaders.compileProgram(OpenGL.GL.shaders.compileShader(vertex_shader, GL_VERTEX_SHADER),
OpenGL.GL.shaders.compileShader(fragment_shader, GL_FRAGMENT_SHADER))
VBO = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, VBO)
glBufferData(GL_ARRAY_BUFFER, 72, triangle, GL_STATIC_DRAW)
position = glGetAttribLocation(shader, "position")
glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, 24, ctypes.c_void_p(0))
glEnableVertexAttribArray(position)
color = glGetAttribLocation(shader, "color")
glVertexAttribPointer(color, 3, GL_FLOAT, GL_FALSE, 24, ctypes.c_void_p(12))
glEnableVertexAttribArray(color)
glUseProgram(shader)
glClearColor(0.2, 0.3, 0.2, 1.0)
while not glfw.window_should_close(window):
glfw.poll_events()
glClear(GL_COLOR_BUFFER_BIT)
glDrawArrays(GL_TRIANGLES, 0, 3)
glfw.swap_buffers(window)
glfw.terminate()
if __name__ == "__main__":
main()
위 코드에서 발생하는 오류는 아래와 같다. (파이썬)
Traceback (most recent call last): File "test.py", line 83, in <module> main() File "test.py", line 52, in main OpenGL.GL.shaders.compileShader(fragment_shader, GL_FRAGMENT_SHADER)) File "/usr/local/lib/python3.7/dist-packages/OpenGL/GL/shaders.py", line 211, in compileProgram program.check_validate() File "/usr/local/lib/python3.7/dist-packages/OpenGL/GL/shaders.py", line 112, in check_validate glGetProgramInfoLog( self ), OpenGL.GL.shaders.ShaderValidationError: Validation failure (0):
#include <GLFW/glfw3.h>
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
using namespace std;
static void error_callback(int error, const char* description)
{
fputs(description, stderr);
}
static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
glfwSetWindowShouldClose(window, GL_TRUE);
}
int main(void)
{
GLFWwindow* window;
glfwSetErrorCallback(error_callback);
if (!glfwInit())
exit(EXIT_FAILURE);
// cout << "default shader lang: " << glGetString(GL_SHADING_LANGUAGE_VERSION) << endl;
// select opengl version
// int major, minor, rev;
// glfwGetVersion(&major, &minor, &rev);
// cout << "glfw major.minor " << major << "." << minor << "." << rev << endl;
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
//glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
window = glfwCreateWindow(640, 480, "Simple example", NULL, NULL);
if (!window)
{
glfwTerminate();
exit(EXIT_FAILURE);
}
glfwMakeContextCurrent(window);
cout << "OpenGL shader language version: " << glGetString(GL_SHADING_LANGUAGE_VERSION) << endl;
glfwSetKeyCallback(window, key_callback);
while (!glfwWindowShouldClose(window))
{
float ratio;
int width, height;
glfwGetFramebufferSize(window, &width, &height);
ratio = width / (float) height;
glViewport(0, 0, width, height);
glClear(GL_COLOR_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-ratio, ratio, -1.f, 1.f, 1.f, -1.f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glRotatef((float) glfwGetTime() * 50.f, 0.f, 0.f, 1.f);
glBegin(GL_TRIANGLES);
glColor3f(1.f, 0.f, 0.f);
glVertex3f(-0.6f, -0.4f, 0.f);
glColor3f(0.f, 1.f, 0.f);
glVertex3f(0.6f, -0.4f, 0.f);
glColor3f(0.f, 0.f, 1.f);
glVertex3f(0.f, 0.6f, 0.f);
glEnd();
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwDestroyWindow(window);
glfwTerminate();
exit(EXIT_SUCCESS);
}
위에서 발생하는 오류는 아래와 같다. (C++)
컴파일 옵션: g++ test.cc -o test -lGL -lGLU -lglfw -lX11 -lXxf86vm -lXrandr -lpthread -lXi
GLX: Forward compatibility requested but GLX_ARB_create_context_profile is unavailable
그리고 마지막 문제는 창에 아무것도 출력되지 않는 문제.