Skip to content

PyOpenGL

PyOpenGL is the most common cross platform Python binding to OpenGL and related APIs. The binding is created using the standard ctypes library, and is provided under an extremely liberal BSD-style Open-Source license.

Categories

PyOpenGL_accelerate

pip install PyOpenGL PyOpenGL_accelerate

INFORMATION

참고로 PyOpenGL_accelerate는 PyOpenGL과 완벽하게 호환되며, 기존 PyOpenGL 코드를 수정하지 않고도 성능 개선 효과를 누릴 수 있습니다.

PyOpenGL_accelerateUSE_ACCELERATE환경변수로 ON/OFF 제어할 수 있다.

다음은 OpenGL/acceleratesupport.py 파일 내용이다:

"""Common code for accelerated modules"""
import logging
from OpenGL import _configflags

needed_version = (3, 1, 6)
_log = logging.getLogger("OpenGL.acceleratesupport")
try:
    import OpenGL_accelerate

    if _configflags.USE_ACCELERATE:
        if OpenGL_accelerate.__version_tuple__ < needed_version:
            _log.warning(
                """Incompatible version of OpenGL_accelerate found, need at least %s found %s""",
                needed_version,
                OpenGL_accelerate.__version_tuple__,
            )
            raise ImportError("""Old version of OpenGL_accelerate""")
        ACCELERATE_AVAILABLE = True
        _log.debug("""OpenGL_accelerate module loaded""")
    else:
        raise ImportError("""Acceleration disabled""")
except ImportError as err:
    _log.info("""No OpenGL_accelerate module loaded: %s""", err)
    ACCELERATE_AVAILABLE = False

_configflags.USE_ACCELERATEUSE_ACCELERATE환경변수를 가져오는 코드이다.

Environment Variables

  • PYOPENGL_ERROR_CHECKING = True
  • PYOPENGL_ERROR_LOGGING = False
  • PYOPENGL_ERROR_ON_COPY = False
  • PYOPENGL_ARRAY_SIZE_CHECKING = True
  • PYOPENGL_STORE_POINTERS = True
  • PYOPENGL_USE_ACCELERATE = True - PyOpenGL_accelerate 사용 여부
  • PYOPENGL_CONTEXT_CHECKING = False
  • PYOPENGL_FULL_LOGGING = False
  • PYOPENGL_ALLOW_NUMPY_SCALARS = False
  • PYOPENGL_UNSIGNED_BYTE_IMAGES_AS_STRING = True
  • PYOPENGL_UNSIGNED_BYTE_IMAGES_AS_STRING = True
  • PYOPENGL_PLATFORM = '...' - 플랫폼 지정

Platforms

PlatformPlugin("nt", "OpenGL.platform.win32.Win32Platform")
PlatformPlugin("darwin", "OpenGL.platform.darwin.DarwinPlatform")
PlatformPlugin("linux2", "OpenGL.platform.glx.GLXPlatform")
PlatformPlugin("linux", "OpenGL.platform.glx.GLXPlatform")
PlatformPlugin("glx", "OpenGL.platform.glx.GLXPlatform")
PlatformPlugin("posix", "OpenGL.platform.glx.GLXPlatform")
PlatformPlugin("x11", "OpenGL.platform.glx.GLXPlatform")  # xdg session type
PlatformPlugin("osmesa", "OpenGL.platform.osmesa.OSMesaPlatform")
PlatformPlugin("egl", "OpenGL.platform.egl.EGLPlatform")
PlatformPlugin("wayland", "OpenGL.platform.egl.EGLPlatform")  # xdg session type
PlatformPlugin(
    "xwayland", "OpenGL.platform.egl.EGLPlatform"
)  # xdg session type, but use egl even though normally you'd expect GLX

OpenGL.setPlatform('egl') 같은 식으로 호출하거나 PYOPENGL_PLATFORM환경변수를 설정해도 된다.

Example

pip install PyOpenGL PyOpenGL_accelerate glfw

PyOpenGL_accelerate 는 하드웨어 가속인듯.

샘플 코드:

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);
    }
    """
    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()

큐브를 돌리는 예제

OpenGL with PyOpenGL introduction and creation of Rotating Cube

import pygame
from pygame.locals import *

from OpenGL.GL import *
from OpenGL.GLU import *

verticies = (
    (1, -1, -1),
    (1, 1, -1),
    (-1, 1, -1),
    (-1, -1, -1),
    (1, -1, 1),
    (1, 1, 1),
    (-1, -1, 1),
    (-1, 1, 1)
    )

edges = (
    (0,1),
    (0,3),
    (0,4),
    (2,1),
    (2,3),
    (2,7),
    (6,3),
    (6,4),
    (6,7),
    (5,1),
    (5,4),
    (5,7)
    )


def Cube():
    glBegin(GL_LINES)
    for edge in edges:
        for vertex in edge:
            glVertex3fv(verticies[vertex])
    glEnd()


def main():
    pygame.init()
    display = (800,600)
    pygame.display.set_mode(display, DOUBLEBUF|OPENGL)

    gluPerspective(45, (display[0]/display[1]), 0.1, 50.0)

    glTranslatef(0.0,0.0, -5)

    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()

        glRotatef(1, 3, 1, 1)
        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
        Cube()
        pygame.display.flip()
        pygame.time.wait(10)


main()

Drawing Object

Troubleshooting

__________________ ERROR collecting tester/test_entrypoint.py __________________
tester/test_entrypoint.py:8: in <module>
    from ddrm.entrypoint import main as entrypoint_main
ddrm/entrypoint.py:8: in <module>
    from ddrm.apps import run_app
ddrm/apps/__init__.py:21: in <module>
    from ddrm.apps.player import Player
ddrm/apps/player/__init__.py:10: in <module>
    from OpenGL import GL
/usr/local/lib/python3.11/site-packages/OpenGL/GL/__init__.py:4: in <module>
    from OpenGL.GL.VERSION.GL_1_1 import *
/usr/local/lib/python3.11/site-packages/OpenGL/GL/VERSION/GL_1_1.py:14: in <module>
    from OpenGL.raw.GL.VERSION.GL_1_1 import *
/usr/local/lib/python3.11/site-packages/OpenGL/raw/GL/VERSION/GL_1_1.py:7: in <module>
    from OpenGL.raw.GL import _errors
/usr/local/lib/python3.11/site-packages/OpenGL/raw/GL/_errors.py:4: in <module>
    _error_checker = _ErrorChecker( _p, _p.GL.glGetError )
E   AttributeError: 'NoneType' object has no attribute 'glGetError'

libgl1-mesa-dev 를 설치하면 된다:

apt-get install -y libgl1-mesa-dev

OpenGL.error.Error: Attempt to retrieve context when no valid context

Ubuntu 20.04 이상에서 Wayland 로 실행된다면 다음과 같은 에러가 출력될 수 있다:

pygame-ce 2.4.1 (SDL 2.28.5, Python 3.11.4)
/home/your/Project/ddrm/test.py:13: Warning: PyGame seems to be running through X11 on top of wayland, instead of wayland directly
...
OpenGL.error.Error: Attempt to retrieve context when no valid context

다음과 같이 SDL_VIDEO_X11_FORCE_EGL=1 환경변수 설정하면 된다:

import os
os.environ["SDL_VIDEO_X11_FORCE_EGL"] = "1"

numpy.dtype size changed, may indicate binary incompatibility

('numpy.dtype size changed, may indicate binary incompatibility. Expected 96 from C header, got 88 from PyObject', 1, <OpenGL.platform.baseplatform.glGenTextures object at 0x72b5e17db840>)
Traceback (most recent call last):
  File "/home/your/Project/ddrm/ddrm/apps/__init__.py", line 96, in run_app
    app.start()
  File "/home/your/Project/ddrm/ddrm/apps/player/__init__.py", line 52, in start
    self.on_init()
  File "/home/your/Project/ddrm/ddrm/apps/player/__init__.py", line 73, in on_init
    self._renderer = PygameRenderer()
                     ^^^^^^^^^^^^^^^^
  File "/home/your/Project/ddrm/ddrm/apps/player/renderer.py", line 18, in __init__
    super().__init__()
  File "/home/your/Project/ddrm/.venv/lib/python3.11/site-packages/imgui/integrations/base.py", line 18, in __init__
    self.refresh_font_texture()
  File "/home/your/Project/ddrm/.venv/lib/python3.11/site-packages/imgui/integrations/opengl.py", line 247, in refresh_font_texture
    self._font_texture = gl.glGenTextures(1)
                         ^^^^^^^^^^^^^^^^^^^
  File "src/latebind.pyx", line 39, in OpenGL_accelerate.latebind.LateBind.__call__
  File "src/wrapper.pyx", line 303, in OpenGL_accelerate.wrapper.Wrapper.__call__
  File "src/wrapper.pyx", line 88, in OpenGL_accelerate.wrapper.CArgCalculator.c_call
  File "src/wrapper.pyx", line 69, in OpenGL_accelerate.wrapper.CArgCalculatorElement.c_call
  File "src/wrapper.pyx", line 64, in OpenGL_accelerate.wrapper.CArgCalculatorElement.c_call
  File "src/arraydatatype.pyx", line 355, in OpenGL_accelerate.arraydatatype.SizedOutputOrInput.c_call
  File "src/arraydatatype.pyx", line 224, in OpenGL_accelerate.arraydatatype.ArrayDatatype.c_zeros
  File "src/arraydatatype.pyx", line 69, in OpenGL_accelerate.arraydatatype.HandlerRegistry.c_get_output_handler
  File "src/arraydatatype.pyx", line 80, in OpenGL_accelerate.arraydatatype.HandlerRegistry.c_handler_by_plugin_name
  File "/home/your/Project/ddrm/.venv/lib/python3.11/site-packages/OpenGL/plugins.py", line 18, in load
    return importByName( self.import_path )
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/your/Project/ddrm/.venv/lib/python3.11/site-packages/OpenGL/plugins.py", line 45, in importByName
    module = __import__( ".".join(moduleName), {}, {}, moduleName)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/your/Project/ddrm/.venv/lib/python3.11/site-packages/OpenGL/arrays/numpymodule.py", line 28, in <module>
    from OpenGL_accelerate.numpy_formathandler import NumpyHandler
  File "src/numpy_formathandler.pyx", line 1, in init OpenGL_accelerate.numpy_formathandler
ValueError: ('numpy.dtype size changed, may indicate binary incompatibility. Expected 96 from C header, got 88 from PyObject', 1, <OpenGL.platform.baseplatform.glGenTextures object at 0x72b5e17db840>)

그냥 PyOpenGL_accelerate를 지우자.

See also

Favorite site