class ExportViewTarget(object): def __init__(self, project, filename, parent): self.mutex = QMutex() self.project = project self.filename = filename self.parent = parent self.surface = QOffscreenSurface() self.surface.setFormat(QSurfaceFormat.defaultFormat()) self.surface.create() def destroy(self): self.surface.destroy() def render(self, view, uniforms=dict()): self.project.setCameraMode('view') self.project.setCameraViewRef(view) self.project.setCameraAtOrigin() self.project.render(self.ctx.functions(), view.info.width, view.info.height, uniforms) depth = self.project.renderer.depthmap(self.ctx.functions(), view.info.width, view.info.height) return depth def __enter__(self): self.mutex.lock() self.ctx = QOpenGLContext() self.ctx.setShareContext(self.parent) self.ctx.create() if not self.ctx.makeCurrent(self.surface): raise Exception("cannot make context current") self.project.renderer.lock() def __exit__(self, type, value, tb): self.project.renderer.unlock() self.ctx.doneCurrent() del self.ctx self.mutex.unlock()
def __init__(self, id: str, app: QGuiApplication): assert self._initialized self.id = id self.app = app self.qml_view = QUrl(os.fspath(get_data_path("webview.qml"))) gl_context = QOpenGLContext() gl_context.setFormat(QSurfaceFormat.defaultFormat()) gl_context.create() self.client = Client(id, self.qml_view, gl_context) self.client.disconnected.connect(self.on_disconnected) self.client.start()
class RenderWindow(QWindow): def __init__(self, format): super(RenderWindow, self).__init__() self.setSurfaceType(QWindow.OpenGLSurface) self.setFormat(format) self.context = QOpenGLContext(self) self.context.setFormat(self.requestedFormat()) if not self.context.create(): raise Exception("Unable to create GL context") self.program = None self.timer = None self.angle = 0 def initGl(self): self.program = QOpenGLShaderProgram(self) self.vao = QOpenGLVertexArrayObject() self.vbo = QOpenGLBuffer() format = self.context.format() useNewStyleShader = format.profile() == QSurfaceFormat.CoreProfile # Try to handle 3.0 & 3.1 that do not have the core/compatibility profile # concept 3.2+ has. This may still fail since version 150 (3.2) is # specified in the sources but it's worth a try. if (format.renderableType() == QSurfaceFormat.OpenGL and format.majorVersion() == 3 and format.minorVersion() <= 1): useNewStyleShader = not format.testOption(QSurfaceFormat.DeprecatedFunctions) vertexShader = vertexShaderSource if useNewStyleShader else vertexShaderSource110 fragmentShader = fragmentShaderSource if useNewStyleShader else fragmentShaderSource110 if not self.program.addShaderFromSourceCode(QOpenGLShader.Vertex, vertexShader): raise Exception("Vertex shader could not be added: {} ({})".format(self.program.log(), vertexShader)) if not self.program.addShaderFromSourceCode(QOpenGLShader.Fragment, fragmentShader): raise Exception("Fragment shader could not be added: {} ({})".format(self.program.log(), fragmentShader)) if not self.program.link(): raise Exception("Could not link shaders: {}".format(self.program.log())) self.posAttr = self.program.attributeLocation("posAttr") self.colAttr = self.program.attributeLocation("colAttr") self.matrixUniform = self.program.uniformLocation("matrix") self.vbo.create() self.vbo.bind() self.verticesData = vertices.tobytes() self.colorsData = colors.tobytes() verticesSize = 4 * vertices.size colorsSize = 4 * colors.size self.vbo.allocate(VoidPtr(self.verticesData), verticesSize + colorsSize) self.vbo.write(verticesSize, VoidPtr(self.colorsData), colorsSize) self.vbo.release() vaoBinder = QOpenGLVertexArrayObject.Binder(self.vao) if self.vao.isCreated(): # have VAO support, use it self.setupVertexAttribs() def setupVertexAttribs(self): self.vbo.bind() self.program.setAttributeBuffer(self.posAttr, GL.GL_FLOAT, 0, 2) self.program.setAttributeBuffer(self.colAttr, GL.GL_FLOAT, 4 * vertices.size, 3) self.program.enableAttributeArray(self.posAttr) self.program.enableAttributeArray(self.colAttr) self.vbo.release() def exposeEvent(self, event): if self.isExposed(): self.render() if self.timer is None: self.timer = QTimer(self) self.timer.timeout.connect(self.slotTimer) if not self.timer.isActive(): self.timer.start(10) else: if self.timer and self.timer.isActive(): self.timer.stop() def render(self): if not self.context.makeCurrent(self): raise Exception("makeCurrent() failed") functions = self.context.functions() if self.program is None: functions.glEnable(GL.GL_DEPTH_TEST) functions.glClearColor(0, 0, 0, 1) self.initGl() retinaScale = self.devicePixelRatio() functions.glViewport(0, 0, self.width() * retinaScale, self.height() * retinaScale) functions.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT) self.program.bind() matrix = QMatrix4x4() matrix.perspective(60, 4 / 3, 0.1, 100) matrix.translate(0, 0, -2) matrix.rotate(self.angle, 0, 1, 0) self.program.setUniformValue(self.matrixUniform, matrix) if self.vao.isCreated(): self.vao.bind() else: # no VAO support, set the vertex attribute arrays now self.setupVertexAttribs() functions.glDrawArrays(GL.GL_TRIANGLES, 0, 3) self.vao.release() self.program.release() # swapInterval is 1 by default which means that swapBuffers() will (hopefully) block # and wait for vsync. self.context.swapBuffers(self) self.context.doneCurrent() def slotTimer(self): self.render() self.angle += 1 def glInfo(self): if not self.context.makeCurrent(self): raise Exception("makeCurrent() failed") functions = self.context.functions() text = """Vendor: {}\nRenderer: {}\nVersion: {}\nShading language: {} \nContext Format: {}\n\nSurface Format: {}""".format( functions.glGetString(GL.GL_VENDOR), functions.glGetString(GL.GL_RENDERER), functions.glGetString(GL.GL_VERSION), functions.glGetString(GL.GL_SHADING_LANGUAGE_VERSION), print_surface_format(self.context.format()), print_surface_format(self.format())) self.context.doneCurrent() return text
class OpenGLWidget(QOpenGLWidget, QOpenGLExtraFunctions): def __init__(self, fmt: QSurfaceFormat, parent=None, *args, **kwargs): QOpenGLWidget.__init__(self, parent, *args, **kwargs) QOpenGLExtraFunctions.__init__(self, *args, **kwargs) self.width, self.height = 1280, 720 self.program = QOpenGLShaderProgram() self.vbo = QOpenGLBuffer(QOpenGLBuffer.VertexBuffer) self.ebo = QOpenGLBuffer(QOpenGLBuffer.IndexBuffer) self.vao = QOpenGLVertexArrayObject() self.model_loc = None self.projection_loc = None self.camera_loc = None self.attrib_loc = None self.shape = Cube() self.models = [] self.model = None self.projection = None self.camera = Camera() self.last_pos = QPoint(self.width / 2.0, self.height / 2.0) self.setFormat(fmt) self.context = QOpenGLContext(self) if not self.context.create(): raise RuntimeError("Unable to create GL context") self.timer = QTimer() self.timer.timeout.connect(self.update) self.timer.start(1000.0/FPS) ### QtGL ### def initializeGL(self) -> None: self.context.aboutToBeDestroyed.connect(self.cleanup) self.initializeOpenGLFunctions() self.build_shaders() self.create_vbo() self.glClearColor(0.2, 0.0, 0.2, 0.5) def paintGL(self) -> None: self.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT) self.glEnable(gl.GL_DEPTH_TEST) self.camera.move() self.render() def resizeGL(self, width: int, height: int) -> None: self.width, self.height = width, height self.glViewport(0, 0, width, height) self.projection = pyrr.matrix44.create_perspective_projection_matrix(45, width/height, 0.1, 150) def render(self) -> None: self.program.bind() vao_binder = QOpenGLVertexArrayObject.Binder(self.vao) # TODO: switch to Qt functions (doesn't work) gl.glUniformMatrix4fv(self.projection_loc, 1, gl.GL_FALSE, self.projection) view = self.camera.get_view_matrix() gl.glUniformMatrix4fv(self.camera_loc, 1, gl.GL_FALSE, view) for model in self.models: # TODO: move to entity class ? rotation = pyrr.matrix44.create_from_axis_rotation( pyrr.vector3.create_from_matrix44_translation(model), time.time(), ) scale = pyrr.matrix44.create_from_scale( pyrr.vector3.create_from_matrix44_translation(model) * 0.1, ) rotation = pyrr.matrix44.multiply(scale, rotation) model = pyrr.matrix44.multiply(rotation, model) orbit = pyrr.Matrix44.from_y_rotation(-0.1*time.time()) model = pyrr.matrix44.multiply(model, orbit) gl.glUniformMatrix4fv(self.model_loc, 1, gl.GL_FALSE, model) self.glDrawElements(gl.GL_TRIANGLES, len(self.shape.indices), gl.GL_UNSIGNED_INT, VoidPtr(0)) self.program.release() vao_binder = None ### Helpers ### def build_shaders(self) -> None: if not self.program.addShaderFromSourceFile(QOpenGLShader.Vertex, "shaders/vertex_shader.glsl"): raise FileNotFoundError("Unable to load vertex shader") if not self.program.addShaderFromSourceFile(QOpenGLShader.Fragment, "shaders/fragment_shader.glsl"): raise FileNotFoundError("Unable to load fragment shader") if not self.program.link(): raise RuntimeError("Unable to link shader program") def create_vbo(self) -> None: self.program.bind() # suspicious behaviour ? self.vao.create() vao_binder = QOpenGLVertexArrayObject.Binder(self.vao) self.vbo.create() self.vbo.bind() self.vbo.allocate(self.shape.vertices, self.shape.vertices.nbytes) self.attrib_loc = self.program.attributeLocation("a_position") self.model_loc = self.program.uniformLocation("model") self.projection_loc = self.program.uniformLocation("projection") self.camera_loc = self.program.uniformLocation("camera") for i in range(150): x, y, z = random.normalvariate(0, 15), random.normalvariate(0, 15), random.normalvariate(0, 15) self.models.append(pyrr.matrix44.create_from_translation(pyrr.Vector3([x, y, z]))) self.ebo.create() self.ebo.bind() self.ebo.allocate(self.shape.indices, self.shape.indices.nbytes) float_size = ctypes.sizeof(ctypes.c_float) # (4) null = VoidPtr(0) pointer = VoidPtr(3 * float_size) self.glEnableVertexAttribArray(0) self.glVertexAttribPointer( 0, 3, gl.GL_FLOAT, gl.GL_FALSE, 6 * float_size, null ) self.glEnableVertexAttribArray(1) self.glVertexAttribPointer( 1, 3, gl.GL_FLOAT, gl.GL_FALSE, 6 * float_size, pointer ) self.vao.release() self.vbo.release() self.ebo.release() self.program.release() vao_binder = None ### Events ### def keyPressEvent(self, event): """W: 0x57, A: 0x41, S: 0x53, D: 0x44, space: 0x20, shift: 0x01000020""" # TODO: make keymap if event.key() == int(0x57): self.camera.keyboard_press("FORWARD") elif event.key() == int(0x53): self.camera.keyboard_press("BACKWARD") if event.key() == int(0x41): self.camera.keyboard_press("LEFT") elif event.key() == int(0x44): self.camera.keyboard_press("RIGHT") if event.key() == int(0x20): self.camera.keyboard_press("UP") elif event.key() == int(0x01000020): self.camera.keyboard_press("DOWN") def keyReleaseEvent(self, event): if event.key() == int(0x57): self.camera.keyboard_release("FORWARD") elif event.key() == int(0x53): self.camera.keyboard_release("BACKWARD") if event.key() == int(0x41): self.camera.keyboard_release("LEFT") elif event.key() == int(0x44): self.camera.keyboard_release("RIGHT") if event.key() == int(0x20): self.camera.keyboard_release("UP") elif event.key() == int(0x01000020): self.camera.keyboard_release("DOWN") def mousePressEvent(self, event): self.last_pos = QPoint(event.pos()) event.accept() def mouseMoveEvent(self, event): dx = event.x() - self.last_pos.x() dy = event.y() - self.last_pos.y() if event.buttons(): self.camera.mouse_movement(float(dx), float(dy)) event.accept() self.last_pos = QPoint(event.pos()) def wheelEvent(self, event): if event.type() in (Qt.ScrollBegin, Qt.ScrollEnd): # FIXME: doesn't seem to work, true divide err return degrees = event.delta() / 8 steps = degrees / 15 event.accept() self.camera.scroll_movement(steps) ### Slots ### @Slot() def cleanup(self): if not self.makeCurrent(): raise Exception("Could not make context current") self.vbo.destroy() self.program.bind() self.program.removeAllShaders() self.program.release() del self.program self.program = None self.doneCurrent()
class EventsGL(QOpenGLWidget): "Cube gl widget" def __init__(self, parent=None): QOpenGLWidget.__init__(self, parent) # camera self.camera = QtCamera() self.camera.position = QVector3D(0.0, 0.0, 3.0) self.camera.front = QVector3D(0.0, 0.0, -1.0) self.camera.up = QVector3D(0.0, 1.0, 0.0) self.camera.movementSensitivity = 0.05 # shaders etc tutoTutoDir = os.path.dirname(__file__) tutoPardir = os.path.join(tutoTutoDir, os.pardir) tutoPardir = os.path.realpath(tutoPardir) mediaDir = os.path.join(tutoPardir, "media") shaderDir = os.path.join(mediaDir, "shaders") availableShaders = ["cube"] self.shaders = { name: { "fragment": os.path.join(shaderDir, name + ".frag"), "vertex": os.path.join(shaderDir, name + ".vert") } for name in availableShaders } self.core = "--coreprofile" in QCoreApplication.arguments() imdir = os.path.join(mediaDir, "images") imFName = "im" imageFile1 = os.path.join(imdir, imFName + "0.png") self.image1 = QImage(imageFile1).mirrored() imageFile2 = os.path.join(imdir, imFName + "1.png") self.image2 = QImage(imageFile2).mirrored() # opengl data related self.context = QOpenGLContext() self.vao = QOpenGLVertexArrayObject() self.vbo = QOpenGLBuffer(QOpenGLBuffer.VertexBuffer) self.program = QOpenGLShaderProgram() self.texture1 = None self.texture2 = None self.texUnit1 = 0 self.texUnit2 = 1 # vertex data self.cubeVertices = np.array( [ # pos vec3 || texcoord vec2 -0.5, -0.5, -0.5, 0.0, 0.0, 0.5, -0.5, -0.5, 1.0, 0.0, 0.5, 0.5, -0.5, 1.0, 1.0, 0.5, 0.5, -0.5, 1.0, 1.0, -0.5, 0.5, -0.5, 0.0, 1.0, -0.5, -0.5, -0.5, 0.0, 0.0, -0.5, -0.5, 0.5, 0.0, 0.0, 0.5, -0.5, 0.5, 1.0, 0.0, 0.5, 0.5, 0.5, 1.0, 1.0, 0.5, 0.5, 0.5, 1.0, 1.0, -0.5, 0.5, 0.5, 0.0, 1.0, -0.5, -0.5, 0.5, 0.0, 0.0, -0.5, 0.5, 0.5, 1.0, 0.0, -0.5, 0.5, -0.5, 1.0, 1.0, -0.5, -0.5, -0.5, 0.0, 1.0, -0.5, -0.5, -0.5, 0.0, 1.0, -0.5, -0.5, 0.5, 0.0, 0.0, -0.5, 0.5, 0.5, 1.0, 0.0, 0.5, 0.5, 0.5, 1.0, 0.0, 0.5, 0.5, -0.5, 1.0, 1.0, 0.5, -0.5, -0.5, 0.0, 1.0, 0.5, -0.5, -0.5, 0.0, 1.0, 0.5, -0.5, 0.5, 0.0, 0.0, 0.5, 0.5, 0.5, 1.0, 0.0, -0.5, -0.5, -0.5, 0.0, 1.0, 0.5, -0.5, -0.5, 1.0, 1.0, 0.5, -0.5, 0.5, 1.0, 0.0, 0.5, -0.5, 0.5, 1.0, 0.0, -0.5, -0.5, 0.5, 0.0, 0.0, -0.5, -0.5, -0.5, 0.0, 1.0, -0.5, 0.5, -0.5, 0.0, 1.0, 0.5, 0.5, -0.5, 1.0, 1.0, 0.5, 0.5, 0.5, 1.0, 0.0, 0.5, 0.5, 0.5, 1.0, 0.0, -0.5, 0.5, 0.5, 0.0, 0.0, -0.5, 0.5, -0.5, 0.0, 1.0 ], dtype=ctypes.c_float) # cube worldSpace coordinates self.cubeCoords = [ QVector3D(0.2, 1.1, -1.0), QVector3D(2.0, 5.0, -15.0), QVector3D(-1.5, -2.2, -2.5), QVector3D(-3.8, -2.0, -12.3), QVector3D(2.4, -0.4, -3.5), QVector3D(-1.7, 3.0, -7.5), QVector3D(1.3, -2.0, -2.5), QVector3D(1.5, 2.0, -2.5), QVector3D(1.5, 0.2, -1.5), QVector3D(-1.3, 1.0, -1.5) ] self.rotateVector = QVector3D(0.7, 0.2, 0.5) def loadShader(self, shaderName: str, shaderType: str): "Load shader" shader = self.shaders[shaderName] shaderSourcePath = shader[shaderType] if shaderType == "vertex": shader = QOpenGLShader(QOpenGLShader.Vertex) else: shader = QOpenGLShader(QOpenGLShader.Fragment) # isCompiled = shader.compileSourceFile(shaderSourcePath) if isCompiled is False: print(shader.log()) raise ValueError( "{0} shader {2} known as {1} is not compiled".format( shaderType, shaderName, shaderSourcePath)) return shader def useShaders(self, shaderProgram: QOpenGLShaderProgram, shaders: {"shaderName": ["shaderType"]}, attrLocs: dict): "" print("program shaders: ", shaderProgram.shaders()) for shaderName, shaderTypes in shaders.items(): # if len(shaderTypes) == 2: self.useShaderSingleName(shaderProgram=shaderProgram, shaderName=shaderName, attrLocs=attrLocs) elif len(shaderTypes) == 1: shaderType = shaderTypes[0] if shaderType == "vertex": shader = self.loadVertexShader(shaderName) else: shader = self.loadFragmentShader(shaderName) shaderProgram.addShader(shader) # adding shader self.bindLinkProgram(shaderProgram, attrLocs) def loadVertexShader(self, shaderName: str): "load vertex shader" return self.loadShader(shaderName, "vertex") def loadFragmentShader(self, shaderName: str): "load fragment shader" return self.loadShader(shaderName, "fragment") def getGlInfo(self): "Get opengl info" info = """ Vendor: {0} Renderer: {1} OpenGL Version: {2} Shader Version: {3} """.format(pygl.glGetString(pygl.GL_VENDOR), pygl.glGetString(pygl.GL_RENDERER), pygl.glGetString(pygl.GL_VERSION), pygl.glGetString(pygl.GL_SHADING_LANGUAGE_VERSION)) return info def moveCamera(self, direction: str): "Move camera to certain direction and update gl widget" self.camera.move(direction, deltaTime=0.05) self.update() def turnAround(self, x: float, y: float): "" self.camera.lookAround(xoffset=x, yoffset=y, pitchBound=True) self.update() def rotateCubes(self, xval: float, yval: float, zval: float): "" self.rotateVector.setZ(zval) self.rotateVector.setY(yval) self.rotateVector.setX(xval) self.update() def cleanUpGl(self): "Clean up everything" self.context.makeCurrent() self.vbo.destroy() self.texture1.destroy() self.texture2.destroy() self.vao.destroy() del self.program self.program = None self.doneCurrent() def resizeGL(self, width: int, height: int): "Resize the viewport" funcs = self.context.functions() funcs.glViewport(0, 0, width, height) def initializeGL(self): print('gl initial') print(self.getGlInfo()) # create context and make it current self.context.create() self.context.aboutToBeDestroyed.connect(self.cleanUpGl) # initialize functions funcs = self.context.functions() funcs.initializeOpenGLFunctions() funcs.glClearColor(0.0, 0.4, 0.4, 0) funcs.glEnable(pygl.GL_DEPTH_TEST) funcs.glEnable(pygl.GL_TEXTURE_2D) # create uniform values for shaders # deal with shaders # cube shader self.program = QOpenGLShaderProgram(self.context) vshader = self.loadVertexShader("cube") fshader = self.loadFragmentShader("cube") self.program.addShader(vshader) # adding vertex shader self.program.addShader(fshader) # adding fragment shader self.program.bindAttributeLocation("aPos", 0) self.program.bindAttributeLocation("aTexCoord", 1) isLinked = self.program.link() print("cube shader program is linked: ", isLinked) # bind the program self.program.bind() self.program.setUniformValue('myTexture1', self.texUnit1) self.program.setUniformValue('myTexture2', self.texUnit2) # # deal with vaos and vbo # vbo isVbo = self.vbo.create() isVboBound = self.vbo.bind() floatSize = ctypes.sizeof(ctypes.c_float) # allocate space on vbo buffer self.vbo.allocate(self.cubeVertices.tobytes(), floatSize * self.cubeVertices.size) self.vao.create() vaoBinder = QOpenGLVertexArrayObject.Binder(self.vao) funcs.glEnableVertexAttribArray(0) # viewport funcs.glVertexAttribPointer(0, 3, int(pygl.GL_FLOAT), int(pygl.GL_FALSE), 5 * floatSize, VoidPtr(0)) funcs.glEnableVertexAttribArray(1) funcs.glVertexAttribPointer(1, 2, int(pygl.GL_FLOAT), int(pygl.GL_FALSE), 5 * floatSize, VoidPtr(3 * floatSize)) # deal with textures # first texture self.texture1 = QOpenGLTexture(QOpenGLTexture.Target2D) self.texture1.create() self.texture1.bind(self.texUnit1) self.texture1.setData(self.image1) self.texture1.setMinMagFilters(QOpenGLTexture.Nearest, QOpenGLTexture.Nearest) self.texture1.setWrapMode(QOpenGLTexture.DirectionS, QOpenGLTexture.Repeat) self.texture1.setWrapMode(QOpenGLTexture.DirectionT, QOpenGLTexture.Repeat) # second texture self.texture2 = QOpenGLTexture(QOpenGLTexture.Target2D) self.texture2.create() self.texture2.bind(self.texUnit2) self.texture2.setData(self.image2) self.texture2.setMinMagFilters(QOpenGLTexture.Linear, QOpenGLTexture.Linear) self.texture2.setWrapMode(QOpenGLTexture.DirectionS, QOpenGLTexture.Repeat) self.texture2.setWrapMode(QOpenGLTexture.DirectionT, QOpenGLTexture.Repeat) self.vbo.release() vaoBinder = None print("gl initialized") def paintGL(self): "drawing loop" funcs = self.context.functions() # clean up what was drawn funcs.glClear(pygl.GL_COLOR_BUFFER_BIT | pygl.GL_DEPTH_BUFFER_BIT) self.vao.bind() self.vbo.bind() # actual drawing self.program.bind() # set projection matrix projectionMatrix = QMatrix4x4() projectionMatrix.perspective(self.camera.zoom, self.width() / self.height(), 0.2, 100.0) self.program.setUniformValue('projection', projectionMatrix) # set view/camera matrix viewMatrix = self.camera.getViewMatrix() self.program.setUniformValue('view', viewMatrix) # bind textures for i, pos in enumerate(self.cubeCoords): # cubeModel = QMatrix4x4() cubeModel.translate(pos) angle = 30 * i cubeModel.rotate(angle, self.rotateVector) self.program.setUniformValue("model", cubeModel) self.texture1.bind(self.texUnit1) self.texture2.bind(self.texUnit2) funcs.glDrawArrays(pygl.GL_TRIANGLES, 0, 36) self.vbo.release() self.program.release() self.texture1.release() self.texture2.release()
class RenderWindow(QWindow): def __init__(self, format): super(RenderWindow, self).__init__() self.setSurfaceType(QWindow.OpenGLSurface) self.setFormat(format) self.context = QOpenGLContext(self) self.context.setFormat(self.requestedFormat()) if not self.context.create(): raise Exception("Unable to create GL context") self.program = None self.timer = None self.angle = 0 def initGl(self): self.program = QOpenGLShaderProgram(self) self.vao = QOpenGLVertexArrayObject() self.vbo = QOpenGLBuffer() format = self.context.format() useNewStyleShader = format.profile() == QSurfaceFormat.CoreProfile # Try to handle 3.0 & 3.1 that do not have the core/compatibility profile # concept 3.2+ has. This may still fail since version 150 (3.2) is # specified in the sources but it's worth a try. if (format.renderableType() == QSurfaceFormat.OpenGL and format.majorVersion() == 3 and format.minorVersion() <= 1): useNewStyleShader = not format.testOption(QSurfaceFormat.DeprecatedFunctions) vertexShader = vertexShaderSource if useNewStyleShader else vertexShaderSource110 fragmentShader = fragmentShaderSource if useNewStyleShader else fragmentShaderSource110 if not self.program.addShaderFromSourceCode(QOpenGLShader.Vertex, vertexShader): raise Exception("Vertex shader could not be added: {} ({})".format(self.program.log(), vertexShader)) if not self.program.addShaderFromSourceCode(QOpenGLShader.Fragment, fragmentShader): raise Exception("Fragment shader could not be added: {} ({})".format(self.program.log(), fragmentShader)) if not self.program.link(): raise Exception("Could not link shaders: {}".format(self.program.log())) self.posAttr = self.program.attributeLocation("posAttr") self.colAttr = self.program.attributeLocation("colAttr") self.matrixUniform = self.program.uniformLocation("matrix") self.vbo.create() self.vbo.bind() self.verticesData = vertices.tobytes() self.colorsData = colors.tobytes() verticesSize = 4 * vertices.size colorsSize = 4 * colors.size self.vbo.allocate(VoidPtr(self.verticesData), verticesSize + colorsSize) self.vbo.write(verticesSize, VoidPtr(self.colorsData), colorsSize) self.vbo.release() vaoBinder = QOpenGLVertexArrayObject.Binder(self.vao) if self.vao.isCreated(): # have VAO support, use it self.setupVertexAttribs() def setupVertexAttribs(self): self.vbo.bind() self.program.setAttributeBuffer(self.posAttr, GL.GL_FLOAT, 0, 2) self.program.setAttributeBuffer(self.colAttr, GL.GL_FLOAT, 4 * vertices.size, 3) self.program.enableAttributeArray(self.posAttr) self.program.enableAttributeArray(self.colAttr) self.vbo.release() def exposeEvent(self, event): if self.isExposed(): self.render() if self.timer is None: self.timer = QTimer(self) self.timer.timeout.connect(self.slotTimer) self.timer.start(10) def render(self): if not self.context.makeCurrent(self): raise Exception("makeCurrent() failed") functions = self.context.functions() if self.program is None: functions.glEnable(GL.GL_DEPTH_TEST) functions.glClearColor(0, 0, 0, 1) self.initGl() functions.glViewport(0, 0, self.width(), self.height()) functions.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT) self.program.bind() matrix = QMatrix4x4() matrix.perspective(60, 4 / 3, 0.1, 100) matrix.translate(0, 0, -2) matrix.rotate(self.angle, 0, 1, 0) self.program.setUniformValue(self.matrixUniform, matrix) if self.vao.isCreated(): self.vao.bind() else: # no VAO support, set the vertex attribute arrays now self.setupVertexAttribs() functions.glDrawArrays(GL.GL_TRIANGLES, 0, 3) self.vao.release() self.program.release() # swapInterval is 1 by default which means that swapBuffers() will (hopefully) block # and wait for vsync. self.context.swapBuffers(self) self.context.doneCurrent() def slotTimer(self): self.render() self.angle += 1 def glInfo(self): if not self.context.makeCurrent(self): raise Exception("makeCurrent() failed") functions = self.context.functions() text = "Vendor: {}\nRenderer: {}\nVersion: {}\nShading language: {}".format( functions.glGetString(GL.GL_VENDOR), functions.glGetString(GL.GL_RENDERER), functions.glGetString(GL.GL_VERSION), functions.glGetString(GL.GL_SHADING_LANGUAGE_VERSION)) self.context.doneCurrent() return text
def initialize(self, size: QSize, shareContext: QOpenGLContext) -> None: """ Initialize offscreen renderer. Args: size: The size of the area available for rendering. shareContext: OpenGL context used as a share context. Raises: RuntimeError: If the renderer has already been initialized. """ print(f'QmlOffscreenRenderer.initialize: {size}') if self.initialized: raise RuntimeError('Already initialized') context = QOpenGLContext() context.setShareContext(shareContext) context.setFormat(get_default_format(gles=False)) context.create() assert not context.isOpenGLES(), "We need glGetTexImage from OpenGL" self.size = size # Create offscreen surface with initialized format surface = QOffscreenSurface() surface.setFormat(context.format()) surface.create() self.ctx = RenderContext(context, surface) # Set up quick rendering self._control = control = QQuickRenderControl() self._window = window = QQuickWindow(control) self._cursor = self._window.cursor() # Don't polish/sync/render immediately for better performance, use a timer self._renderTimer = renderTimer = QTimer() renderTimer.setSingleShot(True) renderTimer.setInterval(5) renderTimer.timeout.connect(self._onRenderTimer) self._syncTimer = syncTimer = QTimer() syncTimer.setSingleShot(True) syncTimer.setInterval(5) syncTimer.timeout.connect(self._onSyncTimer) syncTimer.destroyed.connect(self._onSyncTimerDestroyed) # Request to create frame buffer window.sceneGraphInitialized.connect(self._onSceneGraphInitialized) # Request to release frame buffer window.sceneGraphInvalidated.connect(self._onSceneGraphInvalidated) # Only render is needed control.renderRequested.connect(self._onRenderRequested) # Polish, sync & render control.sceneChanged.connect(self._onSceneChanged) self.initialized = True # Attach root item self.rootItem.setParentItem(self._window.contentItem()) self._window.contentItem().forceActiveFocus() self._updateSizes() self.ctx.makeCurrent() self._control.initialize(self.ctx.glContext)
class TriangleGL(QOpenGLWidget): def __init__(self, parent=None): QOpenGLWidget.__init__(self, parent) # shaders etc triangleTutoDir = os.path.dirname(__file__) trianglePardir = os.path.join(triangleTutoDir, os.pardir) mediaDir = os.path.join(trianglePardir, "media") shaderDir = os.path.join(mediaDir, "shaders") availableShaders = ["triangle", "triangle2"] self.shaders = { name: { "fragment": os.path.join(shaderDir, name + ".frag"), "vertex": os.path.join(shaderDir, name + ".vert") } for name in availableShaders } self.core = "--coreprofile" in QCoreApplication.arguments() # opengl data related self.context = QOpenGLContext() self.vao1 = QOpenGLVertexArrayObject() self.vbo1 = QOpenGLBuffer(QOpenGLBuffer.VertexBuffer) self.vao2 = QOpenGLVertexArrayObject() self.vbo2 = QOpenGLBuffer(QOpenGLBuffer.VertexBuffer) self.program1 = QOpenGLShaderProgram() self.program2 = QOpenGLShaderProgram() # some vertex data for corners of triangle self.vertexData1 = np.array( [0.9, 0.9, 0.0, # x, y, z 0.9, 0.7, 0.0, # x, y, z 0.7, 0.9, 0.0], # x, y, z dtype=ctypes.c_float ) self.vertexData2 = np.array( [-0.9, -0.9, 0.0, # x, y, z -0.9, -0.7, 0.0, # x, y, z -0.7, -0.9, 0.0], # x, y, z dtype=ctypes.c_float ) # triangle color self.triangleColor1 = QVector4D(1.0, 0.0, 0.0, 0.0) # yellow triangle self.triangleColor2 = QVector4D( 0.0, 0.0, 0.5, 0.0) # not yellow triangle def loadShader(self, shaderName: str, shaderType: str): "Load shader" shader = self.shaders[shaderName] shaderSourcePath = shader[shaderType] if shaderType == "vertex": shader = QOpenGLShader(QOpenGLShader.Vertex) else: shader = QOpenGLShader(QOpenGLShader.Fragment) # isCompiled = shader.compileSourceFile(shaderSourcePath) if isCompiled is False: print(shader.log()) raise ValueError( "{0} shader {2} known as {1} is not compiled".format( shaderType, shaderName, shaderSourcePath ) ) return shader def loadVertexShader(self, shaderName: str): "load vertex shader" return self.loadShader(shaderName, "vertex") def loadFragmentShader(self, shaderName: str): "load fragment shader" return self.loadShader(shaderName, "fragment") def getGlInfo(self): "Get opengl info" info = """ Vendor: {0} Renderer: {1} OpenGL Version: {2} Shader Version: {3} """.format( pygl.glGetString(pygl.GL_VENDOR), pygl.glGetString(pygl.GL_RENDERER), pygl.glGetString(pygl.GL_VERSION), pygl.glGetString(pygl.GL_SHADING_LANGUAGE_VERSION) ) return info def initializeGL(self): print('gl initial') print(self.getGlInfo()) # create context and make it current self.context.create() self.context.aboutToBeDestroyed.connect(self.cleanUpGl) # initialize functions funcs = self.context.functions() funcs.initializeOpenGLFunctions() funcs.glClearColor(1, 1, 1, 1) # deal with shaders # first shader shaderName = "triangle" vshader = self.loadVertexShader(shaderName) fshader = self.loadFragmentShader(shaderName) # creating shader program self.program1 = QOpenGLShaderProgram(self.context) self.program1.addShader(vshader) # adding vertex shader self.program1.addShader(fshader) # adding fragment shader # bind attribute to a location self.program1.bindAttributeLocation("aPos", 0) # link shader program1 isLinked = self.program1.link() print("shader program1 is linked: ", isLinked) # bind the program1 self.program1.bind() # specify uniform value colorLoc = self.program1.uniformLocation("color") self.program1.setUniformValue(colorLoc, self.triangleColor1) # second shader shaderName = "triangle2" vshader = self.loadVertexShader(shaderName) fshader = self.loadFragmentShader(shaderName) # self.program2 = QOpenGLShaderProgram(self.context) self.program2.addShader(vshader) # adding vertex shader self.program2.addShader(fshader) # adding fragment shader # bind attribute to a location self.program2.bindAttributeLocation("aPos", 0) # link shader program2 isLinked = self.program2.link() print("shader program2 is linked: ", isLinked) # bind the program2 self.program2.bind() # specify uniform value colorLoc = self.program2.uniformLocation("color") self.program2.setUniformValue(colorLoc, self.triangleColor2) # self.useShader("triangle") # deal with vao and vbo # create vao and vbo # vao isVao = self.vao1.create() vaoBinder = QOpenGLVertexArrayObject.Binder(self.vao1) # vbo isVbo = self.vbo1.create() isBound = self.vbo1.bind() # check if vao and vbo are created print('vao created: ', isVao) print('vbo created: ', isVbo) floatSize = ctypes.sizeof(ctypes.c_float) # allocate space on buffer self.vbo1.allocate(self.vertexData1.tobytes(), floatSize * self.vertexData1.size) funcs.glEnableVertexAttribArray(0) nullptr = VoidPtr(0) funcs.glVertexAttribPointer(0, 3, int(pygl.GL_FLOAT), int(pygl.GL_FALSE), 3 * floatSize, nullptr) self.vbo1.release() vaoBinder = None # second triangle vao vbo # vao isVao = self.vao2.create() vaoBinder = QOpenGLVertexArrayObject.Binder(self.vao2) # vbo isVbo = self.vbo2.create() isBound = self.vbo2.bind() # check if vao and vbo are created print('vao created: ', isVao) print('vbo created: ', isVbo) floatSize = ctypes.sizeof(ctypes.c_float) # allocate space on buffer self.vbo2.allocate(self.vertexData2.tobytes(), floatSize * self.vertexData2.size) funcs.glEnableVertexAttribArray(0) nullptr = VoidPtr(0) funcs.glVertexAttribPointer(0, 3, int(pygl.GL_FLOAT), int(pygl.GL_FALSE), 3 * floatSize, nullptr) self.vbo2.release() self.program2.release() def cleanUpGl(self): "Clean up everything" self.context.makeCurrent() self.vbo.destroy() del self.program self.program = None self.doneCurrent() def resizeGL(self, width: int, height: int): "Resize the viewport" funcs = self.context.functions() funcs.glViewport(0, 0, width, height) def paintGL(self): "drawing loop" funcs = self.context.functions() # clean up what was drawn funcs.glClear(pygl.GL_COLOR_BUFFER_BIT) # actual drawing vaoBinder = QOpenGLVertexArrayObject.Binder(self.vao1) self.program1.bind() funcs.glDrawArrays(pygl.GL_TRIANGLES, # mode 0, # first 3) # count vaoBinder = None self.program1.release() vaoBinder = QOpenGLVertexArrayObject.Binder(self.vao2) self.program2.bind() funcs.glDrawArrays(pygl.GL_TRIANGLES, # mode 0, # first 3) # count vaoBinder = None self.program2.release()
class TextureGL(QOpenGLWidget): "Texture loading opengl widget" def __init__(self, parent=None): "Constructor" QOpenGLWidget.__init__(self, parent) tutoTutoDir = os.path.dirname(__file__) tutoPardir = os.path.join(tutoTutoDir, os.pardir) tutoPardir = os.path.realpath(tutoPardir) mediaDir = os.path.join(tutoPardir, "media") shaderDir = os.path.join(mediaDir, "shaders") # availableShaders = ["texture"] self.shaders = { name: { "fragment": os.path.join(shaderDir, name + ".frag"), "vertex": os.path.join(shaderDir, name + ".vert") } for name in availableShaders } imdir = os.path.join(mediaDir, "images") imFName = "im" imageFile = os.path.join(imdir, imFName + "0.png") self.image = QImage(imageFile).mirrored() self.core = "--coreprofile" in QCoreApplication.arguments() # opengl data related self.context = QOpenGLContext() self.program = QOpenGLShaderProgram() self.vao = QOpenGLVertexArrayObject() self.vbo = QOpenGLBuffer(QOpenGLBuffer.VertexBuffer) self.texture = None self.indices = np.array( [ 0, 1, 3, # first triangle 1, 2, 3 # second triangle ], dtype=ctypes.c_uint) # vertex data of the panel that would hold the image self.vertexData = np.array( [ # viewport position || texture coords 0.5, 0.5, 0.0, 1.0, 1.0, # top right 0.5, -0.5, 0.0, 1.0, 0.0, # bottom right -0.5, -0.5, 0.0, 0.0, 0.0, # bottom left -0.5, 0.5, 0.0, 0.0, 1.0 # top left ], dtype=ctypes.c_float) def loadShader(self, shaderName: str, shaderType: str): "Load shader" shader = self.shaders[shaderName] shaderSourcePath = shader[shaderType] if shaderType == "vertex": shader = QOpenGLShader(QOpenGLShader.Vertex) else: shader = QOpenGLShader(QOpenGLShader.Fragment) # isCompiled = shader.compileSourceFile(shaderSourcePath) if isCompiled is False: print(shader.log()) raise ValueError( "{0} shader {2} known as {1} is not compiled".format( shaderType, shaderName, shaderSourcePath)) return shader def loadVertexShader(self, shaderName: str): "load vertex shader" return self.loadShader(shaderName, "vertex") def loadFragmentShader(self, shaderName: str): "load fragment shader" return self.loadShader(shaderName, "fragment") def getGlInfo(self): "Get opengl info" info = """ Vendor: {0} Renderer: {1} OpenGL Version: {2} Shader Version: {3} """.format(pygl.glGetString(pygl.GL_VENDOR), pygl.glGetString(pygl.GL_RENDERER), pygl.glGetString(pygl.GL_VERSION), pygl.glGetString(pygl.GL_SHADING_LANGUAGE_VERSION)) return info def cleanUpGl(self): "Clean up everything" self.context.makeCurrent() del self.program self.program = None self.texture.release() self.doneCurrent() def resizeGL(self, width: int, height: int): "Resize the viewport" funcs = self.context.functions() funcs.glViewport(0, 0, width, height) def initializeGL(self): "Initialize opengl " print('gl initial') print(self.getGlInfo()) # create context and make it current self.context.create() self.context.aboutToBeDestroyed.connect(self.cleanUpGl) # initialize functions funcs = self.context.functions() funcs.initializeOpenGLFunctions() funcs.glClearColor(1, 0, 1, 1) # shader shaderName = "texture" vshader = self.loadVertexShader(shaderName) fshader = self.loadFragmentShader(shaderName) # create shader program self.program = QOpenGLShaderProgram(self.context) self.program.addShader(vshader) self.program.addShader(fshader) # bind attribute location self.program.bindAttributeLocation("aPos", 0) self.program.bindAttributeLocation("aTexCoord", 1) # link shader program isLinked = self.program.link() print("shader program is linked: ", isLinked) # activate shader program to set uniform an attribute values self.program.bind() self.program.setUniformValue('myTexture', 0) # vbo isVbo = self.vbo.create() isVboBound = self.vbo.bind() floatSize = ctypes.sizeof(ctypes.c_float) # allocate vbo self.vbo.allocate(self.vertexData.tobytes(), floatSize * self.vertexData.size) # texture new school self.texture = QOpenGLTexture(QOpenGLTexture.Target2D) self.texture.create() # new school self.texture.bind() self.texture.setData(self.image) self.texture.setMinMagFilters(QOpenGLTexture.Linear, QOpenGLTexture.Linear) self.texture.setWrapMode(QOpenGLTexture.DirectionS, QOpenGLTexture.Repeat) self.texture.setWrapMode(QOpenGLTexture.DirectionT, QOpenGLTexture.Repeat) def paintGL(self): "paint gl" funcs = self.context.functions() # clean up what was drawn funcs.glClear(pygl.GL_COLOR_BUFFER_BIT) self.program.bind() self.program.enableAttributeArray(0) self.program.enableAttributeArray(1) floatSize = ctypes.sizeof(ctypes.c_float) # set attribute values self.program.setAttributeBuffer( 0, # viewport position pygl.GL_FLOAT, # coord type 0, # offset 3, 5 * floatSize) self.program.setAttributeBuffer( 1, # viewport position pygl.GL_FLOAT, # coord type 3 * floatSize, # offset 2, 5 * floatSize) # bind texture self.texture.bind() funcs.glDrawElements(pygl.GL_TRIANGLES, self.indices.size, pygl.GL_UNSIGNED_INT, self.indices.tobytes())
class GLWin(QWindow): requestRender = Signal() def __init__(self, parent=None): QWindow.__init__(self, parent) self.setSurfaceType(QSurface.OpenGLSurface) self.gl_format = QSurfaceFormat() self.gl_format.setRenderableType(QSurfaceFormat.OpenGL) self.gl_format.setProfile(QSurfaceFormat.CoreProfile) self.gl_format.setVersion(4, 1) self.setFormat(self.gl_format) if self.supportsOpenGL(): print("OpenGL supported !") self.mouse_x = 0 self.mouse_y = 0 self.mouse_init = False self.animating = False self.requestRender.connect(self.requestUpdate) # self.setMouseGrabEnabled(True) self.mouse_pressed = False def mousePressEvent(self, ev): if ev.button() == Qt.LeftButton: self.mouse_pressed = True def mouseReleaseEvent(self, ev): if ev.button() == Qt.LeftButton: self.mouse_pressed = False self.mouse_init = False def mouseMoveEvent(self, ev): pos = ev.localPos() if self.mouse_pressed: if not self.mouse_init: self.mouse_x = pos.x() self.mouse_y = pos.y() self.mouse_init = True else: dx = self.mouse_x - pos.x() dy = self.mouse_y - pos.y() self.mouse_x = pos.x() self.mouse_y = pos.y() handle_mouse(c_float(dx), c_float(dy), c_float(0.001)) def start(self): self.animating = True self.renderLater() def render(self): display_loop(c_double(0.0)) def renderLater(self): self.requestRender.emit() def renderNow(self): if not self.isExposed(): return self.render() self.gl_context.swapBuffers(self) if self.animating: self.renderLater() def init_context(self): self.gl_context = QOpenGLContext(self) self.gl_context.setFormat(self.gl_format) if self.gl_context.create(): print("Context created !") if self.gl_context.makeCurrent(self): print("Context made current !") def init_scene(self): width = c_double(self.size().width()) height = c_double(self.size().height()) dpi_ratio = c_double(self.devicePixelRatio()) load_gl_symbol() init_gl(width, height, dpi_ratio) print_gl_info() init_scene(width, height, dpi_ratio) def resize(self): if self.isExposed(): width = c_double(self.size().width()) height = c_double(self.size().height()) dpi_ratio = c_double(self.devicePixelRatio()) resize_window(width, height, dpi_ratio) def event(self, ev): if ev.type() == QEvent.UpdateRequest: self.renderNow() return True else: return super().event(ev) def exposeEvent(self, ev): self.renderLater() def resizeEvent(self, ev): self.resize() self.renderLater()
def initialize(self, size: QSize, shareContext: QOpenGLContext) -> None: """ Initialize offscreen renderer. Args: size: The size of the area available for rendering. shareContext: OpenGL context used as a share context. Raises: RuntimeError: If the renderer has already been initialized. """ print(f'QmlOffscreenRenderer.initialize: {size}') if self.initialized: raise RuntimeError('Already initialized') format = QSurfaceFormat() format.setDepthBufferSize(16) format.setStencilBufferSize(8) context = QOpenGLContext() context.setFormat(format) context.setShareContext(shareContext) context.create() self.size = size self.context = context # Create offscreen surface with initialized format self._surface = surface = QOffscreenSurface() surface.setFormat(context.format()) surface.create() # Set up quick rendering self._control = control = QQuickRenderControl() self._window = window = QQuickWindow(control) self._engine = engine = QQmlEngine() if not engine.incubationController(): engine.setIncubationController(window.incubationController()) # Don't polish/sync/render immediately for better performance, use a timer self._renderTimer = renderTimer = QTimer() renderTimer.setSingleShot(True) renderTimer.setInterval(5) renderTimer.timeout.connect(self._onRenderTimer) self._syncTimer = syncTimer = QTimer() syncTimer.setSingleShot(True) syncTimer.setInterval(5) syncTimer.timeout.connect(self._onSyncTimer) syncTimer.destroyed.connect(self._onSyncTimerDestroyed) # Request to create frame buffer window.sceneGraphInitialized.connect(self._onSceneGraphInitialized) # Request to release frame buffer window.sceneGraphInvalidated.connect(self._onSceneGraphInvalidated) # Only render is needed control.renderRequested.connect(self._onRenderRequested) # Polish, sync & render control.sceneChanged.connect(self._onSceneChanged) self.initialized = True # Load QML component self._component = component = QQmlComponent(self._engine, self.qmlUrl) if component.isLoading(): component.statusChanged.connect(self._onComponentStatusChanged) else: self._attachRootItem()
class RectangleGL(QOpenGLWidget): "Texture loading opengl widget" def __init__(self, parent=None): "Constructor" QOpenGLWidget.__init__(self, parent) tutoTutoDir = os.path.dirname(__file__) tutoPardir = os.path.join(tutoTutoDir, os.pardir) tutoPardir = os.path.realpath(tutoPardir) mediaDir = os.path.join(tutoPardir, "media") shaderDir = os.path.join(mediaDir, "shaders") # availableShaders = ["rectangle", "triangle"] self.shaders = { name: { "fragment": os.path.join(shaderDir, name + ".frag"), "vertex": os.path.join(shaderDir, name + ".vert") } for name in availableShaders } self.core = "--coreprofile" in QCoreApplication.arguments() # opengl data related self.context = QOpenGLContext() self.program = QOpenGLShaderProgram() self.vao = QOpenGLVertexArrayObject() self.vbo = QOpenGLBuffer(QOpenGLBuffer.VertexBuffer) self.indices = np.array( [ 0, 1, 3, # first triangle 1, 2, 3 # second triangle ], dtype=ctypes.c_uint) # vertex data of the panel that would hold the image self.vertexData = np.array( [ # viewport position || colors || texture coords 0.5, 0.5, 0.0, # top right 0.5, -0.5, 0.0, # bottom right -0.5, -0.5, 0.0, # bottom left -0.5, 0.5, 0.0, # top left ], dtype=ctypes.c_float) self.rectColor = QVector4D(0.0, 1.0, 1.0, 0.0) def loadShader(self, shaderName: str, shaderType: str): "Load shader" shader = self.shaders[shaderName] shaderSourcePath = shader[shaderType] if shaderType == "vertex": shader = QOpenGLShader(QOpenGLShader.Vertex) else: shader = QOpenGLShader(QOpenGLShader.Fragment) # isCompiled = shader.compileSourceFile(shaderSourcePath) if isCompiled is False: print(shader.log()) raise ValueError( "{0} shader {2} known as {1} is not compiled".format( shaderType, shaderName, shaderSourcePath)) return shader def loadVertexShader(self, shaderName: str): "load vertex shader" return self.loadShader(shaderName, "vertex") def loadFragmentShader(self, shaderName: str): "load fragment shader" return self.loadShader(shaderName, "fragment") def getGlInfo(self): "Get opengl info" info = """ Vendor: {0} Renderer: {1} OpenGL Version: {2} Shader Version: {3} """.format(pygl.glGetString(pygl.GL_VENDOR), pygl.glGetString(pygl.GL_RENDERER), pygl.glGetString(pygl.GL_VERSION), pygl.glGetString(pygl.GL_SHADING_LANGUAGE_VERSION)) return info def cleanUpGl(self): "Clean up everything" self.context.makeCurrent() del self.program self.program = None self.vbo.release() self.doneCurrent() def resizeGL(self, width: int, height: int): "Resize the viewport" funcs = self.context.functions() funcs.glViewport(0, 0, width, height) def initializeGL(self): "Initialize opengl " print('gl initial') print(self.getGlInfo()) # create context and make it current self.context.create() self.context.aboutToBeDestroyed.connect(self.cleanUpGl) # initialize functions funcs = self.context.functions() funcs.initializeOpenGLFunctions() funcs.glClearColor(1, 1, 1, 1) # shader shaderName = "triangle" vshader = self.loadVertexShader(shaderName) fshader = self.loadFragmentShader(shaderName) # create shader program self.program = QOpenGLShaderProgram(self.context) self.program.addShader(vshader) self.program.addShader(fshader) # bind attribute location self.program.bindAttributeLocation("aPos", 0) # link shader program isLinked = self.program.link() print("shader program is linked: ", isLinked) # activate shader program to set uniform an attribute values self.program.bind() # specify uniform value colorLoc = self.program.uniformLocation("color") self.program.setUniformValue(colorLoc, self.rectColor) # vao, vbo, texture # vao isVao = self.vao.create() vaoBinder = QOpenGLVertexArrayObject.Binder(self.vao) # vbo isVbo = self.vbo.create() isVboBound = self.vbo.bind() floatSize = ctypes.sizeof(ctypes.c_float) # allocate vbo self.vbo.allocate(self.vertexData.tobytes(), floatSize * self.vertexData.size) print("vao created: ", isVao) print("vbo created: ", isVbo) print("vbo bound: ", isVboBound) # dealing with attributes # vertex array position funcs.glVertexAttribPointer(0, 3, int(pygl.GL_FLOAT), int(pygl.GL_FALSE), 3 * floatSize, VoidPtr(0)) funcs.glEnableVertexAttribArray(0) self.vbo.release() vaoBinder = None def paintGL(self): "paint gl" funcs = self.context.functions() # clean up what was drawn funcs.glClear(pygl.GL_COLOR_BUFFER_BIT) # bind texture vaoBinder = QOpenGLVertexArrayObject.Binder(self.vao) self.program.bind() # draw stuff funcs.glDrawElements(pygl.GL_TRIANGLES, self.indices.size, pygl.GL_UNSIGNED_INT, self.indices.tobytes()) # VoidPtr(self.indices.tobytes() * ctypes.sizeof(ctypes.c_uint))) vaoBinder = None self.program.release()