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 GLWidget(QOpenGLWidget, QOpenGLFunctions): xRotationChanged = Signal(int) yRotationChanged = Signal(int) zRotationChanged = Signal(int) def __init__(self, parent=None): QOpenGLWidget.__init__(self, parent) QOpenGLFunctions.__init__(self) self.core = "--coreprofile" in QCoreApplication.arguments() self.xRot = 0 self.yRot = 0 self.zRot = 0 self.lastPos = 0 self.logo = Logo() self.vao = QOpenGLVertexArrayObject() self.logoVbo = QOpenGLBuffer() self.program = QOpenGLShaderProgram() self.projMatrixLoc = 0 self.mvMatrixLoc = 0 self.normalMatrixLoc = 0 self.lightPosLoc = 0 self.proj = QMatrix4x4() self.camera = QMatrix4x4() self.world = QMatrix4x4() self.transparent = "--transparent" in QCoreApplication.arguments() if self.transparent: fmt = self.format() fmt.setAlphaBufferSize(8) self.setFormat(fmt) def xRotation(self): return self.xRot def yRotation(self): return self.yRot def zRotation(self): return self.zRot def minimumSizeHint(self): return QSize(50, 50) def sizeHint(self): return QSize(400, 400) def normalizeAngle(self, angle): while angle < 0: angle += 360 * 16 while angle > 360 * 16: angle -= 360 * 16 return angle def setXRotation(self, angle): angle = self.normalizeAngle(angle) if angle != self.xRot: self.xRot = angle self.emit(SIGNAL("xRotationChanged(int)"), angle) self.update() def setYRotation(self, angle): angle = self.normalizeAngle(angle) if angle != self.yRot: self.yRot = angle self.emit(SIGNAL("yRotationChanged(int)"), angle) self.update() def setZRotation(self, angle): angle = self.normalizeAngle(angle) if angle != self.zRot: self.zRot = angle self.emit(SIGNAL("zRotationChanged(int)"), angle) self.update() def cleanup(self): self.makeCurrent() self.logoVbo.destroy() del self.program self.program = None self.doneCurrent() def vertexShaderSourceCore(self): return """#version 150 in vec4 vertex; in vec3 normal; out vec3 vert; out vec3 vertNormal; uniform mat4 projMatrix; uniform mat4 mvMatrix; uniform mat3 normalMatrix; void main() { vert = vertex.xyz; vertNormal = normalMatrix * normal; gl_Position = projMatrix * mvMatrix * vertex; }""" def fragmentShaderSourceCore(self): return """#version 150 in highp vec3 vert; in highp vec3 vertNormal; out highp vec4 fragColor; uniform highp vec3 lightPos; void main() { highp vec3 L = normalize(lightPos - vert); highp float NL = max(dot(normalize(vertNormal), L), 0.0); highp vec3 color = vec3(0.39, 1.0, 0.0); highp vec3 col = clamp(color * 0.2 + color * 0.8 * NL, 0.0, 1.0); fragColor = vec4(col, 1.0); }""" def vertexShaderSource(self): return """attribute vec4 vertex; attribute vec3 normal; varying vec3 vert; varying vec3 vertNormal; uniform mat4 projMatrix; uniform mat4 mvMatrix; uniform mat3 normalMatrix; void main() { vert = vertex.xyz; vertNormal = normalMatrix * normal; gl_Position = projMatrix * mvMatrix * vertex; }""" def fragmentShaderSource(self): return """varying highp vec3 vert; varying highp vec3 vertNormal; uniform highp vec3 lightPos; void main() { highp vec3 L = normalize(lightPos - vert); highp float NL = max(dot(normalize(vertNormal), L), 0.0); highp vec3 color = vec3(0.39, 1.0, 0.0); highp vec3 col = clamp(color * 0.2 + color * 0.8 * NL, 0.0, 1.0); gl_FragColor = vec4(col, 1.0); }""" def initializeGL(self): self.context().aboutToBeDestroyed.connect(self.cleanup) self.initializeOpenGLFunctions() self.glClearColor(0, 0, 0, 1) self.program = QOpenGLShaderProgram() if self.core: self.vertexShader = self.vertexShaderSourceCore() self.fragmentShader = self.fragmentShaderSourceCore() else: self.vertexShader = self.vertexShaderSource() self.fragmentShader = self.fragmentShaderSource() self.program.addShaderFromSourceCode(QOpenGLShader.Vertex, self.vertexShader) self.program.addShaderFromSourceCode(QOpenGLShader.Fragment, self.fragmentShader) self.program.bindAttributeLocation("vertex", 0) self.program.bindAttributeLocation("normal", 1) self.program.link() self.program.bind() self.projMatrixLoc = self.program.uniformLocation("projMatrix") self.mvMatrixLoc = self.program.uniformLocation("mvMatrix") self.normalMatrixLoc = self.program.uniformLocation("normalMatrix") self.lightPosLoc = self.program.uniformLocation("lightPos") self.vao.create() vaoBinder = QOpenGLVertexArrayObject.Binder(self.vao) self.logoVbo.create() self.logoVbo.bind() float_size = ctypes.sizeof(ctypes.c_float) self.logoVbo.allocate(self.logo.constData(), self.logo.count() * float_size) self.setupVertexAttribs() self.camera.setToIdentity() self.camera.translate(0, 0, -1) self.program.setUniformValue(self.lightPosLoc, QVector3D(0, 0, 70)) self.program.release() vaoBinder = None def setupVertexAttribs(self): self.logoVbo.bind() f = QOpenGLContext.currentContext().functions() f.glEnableVertexAttribArray(0) f.glEnableVertexAttribArray(1) float_size = ctypes.sizeof(ctypes.c_float) null = VoidPtr(0) pointer = VoidPtr(3 * float_size) f.glVertexAttribPointer(0, 3, int(GL.GL_FLOAT), int(GL.GL_FALSE), 6 * float_size, null) f.glVertexAttribPointer(1, 3, int(GL.GL_FLOAT), int(GL.GL_FALSE), 6 * float_size, pointer) self.logoVbo.release() def paintGL(self): self.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT) self.glEnable(GL.GL_DEPTH_TEST) self.glEnable(GL.GL_CULL_FACE) self.world.setToIdentity() self.world.rotate(180 - (self.xRot / 16), 1, 0, 0) self.world.rotate(self.yRot / 16, 0, 1, 0) self.world.rotate(self.zRot / 16, 0, 0, 1) vaoBinder = QOpenGLVertexArrayObject.Binder(self.vao) self.program.bind() self.program.setUniformValue(self.projMatrixLoc, self.proj) self.program.setUniformValue(self.mvMatrixLoc, self.camera * self.world) normalMatrix = self.world.normalMatrix() self.program.setUniformValue(self.normalMatrixLoc, normalMatrix) self.glDrawArrays(GL.GL_TRIANGLES, 0, self.logo.vertexCount()) self.program.release() vaoBinder = None def resizeGL(self, width, height): self.proj.setToIdentity() self.proj.perspective(45, width / height, 0.01, 100) def mousePressEvent(self, event): self.lastPos = QPoint(event.pos()) def mouseMoveEvent(self, event): dx = event.x() - self.lastPos.x() dy = event.y() - self.lastPos.y() if event.buttons() & Qt.LeftButton: self.setXRotation(self.xRot + 8 * dy) self.setYRotation(self.yRot + 8 * dx) elif event.buttons() & Qt.RightButton: self.setXRotation(self.xRot + 8 * dy) self.setZRotation(self.zRot + 8 * dx) self.lastPos = QPoint(event.pos())
class TGLViewport(QOpenGLWidget, QOpenGLFunctions): def __init__(self, parent=None): QOpenGLWidget.__init__(self, parent) QOpenGLFunctions.__init__(self) self.setMinimumSize(32, 32) self.info = "" self._supported_images = [ "TGA", "PNG", "JPG", "JPEG", "TIF", "TIFF", "BMP", "DDS" ] # indices indices = [0, 1, 3, 1, 2, 3] self._indices = array('I', indices) # vertices # 3 position | 2 texture coord vertex = [ 1.0, 1.0, 0.0, 1.0, 1.0, # top right 1.0, -1.0, 0.0, 1.0, 0.0, # bottom right -1.0, -1.0, 0.0, 0.0, 0.0, # bottom left -1.0, 1.0, 0.0, 0.0, 1.0 # top left ] self._vertex = array('f', vertex) # opengl data related self._program = QOpenGLShaderProgram() self._program_bg = QOpenGLShaderProgram() self._vao = QOpenGLVertexArrayObject() self._vbo = QOpenGLBuffer(QOpenGLBuffer.VertexBuffer) self._texture = None self._texture_size = (1, 1) self._location = () self._colors_default = (QColor.fromRgbF(0.65, 0.65, 0.65, 1.0), QColor.fromRgbF(0.90, 0.90, 0.90, 1.0)) self._u_colors = self._colors_default self._height = QVector4D(0, self.height(), 0, 0) self._u_channels = QMatrix4x4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0) def initializeGL(self): # Set up the rendering context, define display lists etc. self.initializeOpenGLFunctions() self.glClearColor(0.2, 0.0, 0.2, 0.0) self.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA) # shader code (OpenGL ES) # texture vs_source_es = """ attribute highp vec3 Pos; attribute highp vec2 UV; uniform highp vec2 Scale; varying highp vec2 oUV; void main() { gl_Position = vec4(Pos * vec3(Scale, 1.0), 1.0); oUV = UV; } """ ps_source_es = """ varying highp vec2 oUV; uniform sampler2D Texture; uniform highp mat4 Channels; uniform highp vec4 Add; void main() { gl_FragColor = texture2D(Texture, oUV * vec2(1.0, -1.0)) * Channels; gl_FragColor.a += (1.0 - Channels[3][3]); } """ # background vs_grid_es = """ attribute highp vec3 Pos; void main() { gl_Position = vec4(Pos, 1.0); } """ ps_grid_es = """ uniform highp vec4 Color1; uniform highp vec4 Color2; uniform highp vec4 Height; void main() { highp vec2 a = floor((Height.xy - gl_FragCoord.xy) / 64.0); highp float even = mod(a.x + a.y, 2.0); highp vec3 c = mix(Color1.rgb, Color2.rgb, even); gl_FragColor = vec4(c, 1); } """ # program - texture # shader vs = self.__create_shader(QOpenGLShader.Vertex, vs_source_es) fs = self.__create_shader(QOpenGLShader.Fragment, ps_source_es) # program self._program = QOpenGLShaderProgram(self.context()) self._program.addShader(vs) self._program.addShader(fs) # attribute location self._program.bindAttributeLocation("Pos", 0) self._program.bindAttributeLocation("UV", 1) # link program r = self._program.link() # program - background # shader vs = self.__create_shader(QOpenGLShader.Vertex, vs_grid_es) fs = self.__create_shader(QOpenGLShader.Fragment, ps_grid_es) # program self._program_bg = QOpenGLShaderProgram(self.context()) self._program_bg.addShader(vs) self._program_bg.addShader(fs) # attribute location self._program_bg.bindAttributeLocation("Pos", 0) # link program r = self._program_bg.link() # uniform locations self._location = ( self._program.uniformLocation("Scale"), self._program.uniformLocation("Channels"), self._program_bg.uniformLocation("Color1"), self._program_bg.uniformLocation("Color2"), self._program_bg.uniformLocation("Height"), ) # vao r = self._vao.create() r = self._vao.bind() # vbo r = self._vbo.create() self._vbo.setUsagePattern(QOpenGLBuffer.StaticDraw) r = self._vbo.bind() sz_float = ctypes.sizeof(ctypes.c_float) self._vbo.allocate(self._vertex.tobytes(), sz_float * len(self._vertex)) self._vao.release() # texture self.set_texture(r"C:") def resizeGL(self, width, height): self.__update_scale(width, height) self._height = QVector4D(0, self.height(), 0, 0) self.glViewport(0, 0, width, height) def paintGL(self): SZ_FLOAT = ctypes.sizeof(ctypes.c_float) # draw the scene self.glClear(GL.GL_COLOR_BUFFER_BIT) self._vao.bind() # background self._program_bg.bind() self._program.setAttributeBuffer(0, GL.GL_FLOAT, 0, 3, 5 * SZ_FLOAT) self._program.enableAttributeArray(0) self._program.setUniformValue(self._location[2], self._u_colors[0]) self._program.setUniformValue(self._location[3], self._u_colors[1]) self._program.setUniformValue(self._location[4], self._height) self.glDrawElements(GL.GL_TRIANGLES, len(self._indices), GL.GL_UNSIGNED_INT, self._indices.tobytes()) # texture if self._texture is None: return self.glEnable(GL.GL_BLEND) self._program.bind() self._program.setAttributeBuffer(1, GL.GL_FLOAT, 3 * SZ_FLOAT, 2, 5 * SZ_FLOAT) self._program.enableAttributeArray(1) self._program.setUniformValue(self._location[0], *self._scale) self._program.setUniformValue(self._location[1], self._u_channels) self._texture.bind() self.glDrawElements(GL.GL_TRIANGLES, len(self._indices), GL.GL_UNSIGNED_INT, self._indices.tobytes()) self.glDisable(GL.GL_BLEND) @staticmethod def __create_shader(type_: QOpenGLShader.ShaderType, source): shader = QOpenGLShader(type_) r = shader.compileSourceCode(source) if not r: print(shader.log()) return shader def __update_scale(self, width, height): # calc texture scale if self._texture_size[0] < width and self._texture_size[1] < height: self._scale = (self._texture_size[0] / width, self._texture_size[1] / height) else: tw = width th = self._texture_size[1] / self._texture_size[0] * tw if th > height: th = height tw = th * self._texture_size[0] / self._texture_size[1] self._scale = (tw / width, th / height) def get_gl_info(self): self.makeCurrent() info = """ Vendor: {0} Renderer: {1} OpenGL Version: {2} Shader Version: {3} """.format(self.glGetString(GL.GL_VENDOR), self.glGetString(GL.GL_RENDERER), self.glGetString(GL.GL_VERSION), self.glGetString(GL.GL_SHADING_LANGUAGE_VERSION)) return info def set_channels(self, r, g, b, a): r, g, b, a = float(r), float(g), float(b), float(a) s = r + g + b if s > 1.1: self._u_channels = QMatrix4x4(r, 0, 0, 0, 0, g, 0, 0, 0, 0, b, 0, 0, 0, 0, a) elif s < 0.1: self._u_channels = QMatrix4x4(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, a, a, a, 0) elif a: self._u_channels = QMatrix4x4(r, 0, 0, 0, 0, g, 0, 0, 0, 0, b, 0, 0, 0, 0, a) else: self._u_channels = QMatrix4x4(r, r, r, 0, g, g, g, 0, b, b, b, 0, 0, 0, 0, a) # redraw self.update() def set_texture(self, filename): # load image p = Path(filename) suffix = p.suffix[1:].upper() try: if p.is_file() and suffix in self._supported_images: pim = Image.open(filename) img = ImageQt(pim) self.info = f"{pim.format} - {pim.size} - {pim.mode} " else: ico = QFileIconProvider().icon(QFileInfo(filename)) pix = ico.pixmap(256, 256) img = pix.toImage() self.info = "not an image " except UnidentifiedImageError as e: print("UnidentifiedImageError:\n", e, flush=True) return self._texture_size = img.width(), img.height() self.__update_scale(self.width(), self.height()) # create texture self.makeCurrent() self._texture = QOpenGLTexture(QOpenGLTexture.Target2D) self._texture.create() self._texture.bind() self._texture.setMinMagFilters(QOpenGLTexture.LinearMipMapLinear, QOpenGLTexture.Linear) self._texture.setWrapMode(QOpenGLTexture.DirectionS, QOpenGLTexture.Repeat) self._texture.setWrapMode(QOpenGLTexture.DirectionT, QOpenGLTexture.Repeat) self._texture.setData(img) self._texture.release() # redraw self.update() def set_colors(self, checkerboard, color1, color2): if checkerboard: self._u_colors = self._colors_default else: self._u_colors = (color1, color2) self.update()
class SquareObject(QObject): def __init__(self, parent=None): super(SquareObject, self).__init__(parent=parent) self.initialize_shader_program() self.initialize_geometry() self.initialize_geometry_on_gpu() self.initialize_texture_on_gpu() def destroy(self): self.vao.release() self.vao.destroy() self.vbo.release() self.vbo.destroy() self.ebo.release() self.ebo.destroy() self.texture0.release() self.texture0.destroy() self.texture1.release() self.texture1.destroy() def initialize_shader_program(self): self.vertex_shader = """ #version 330 core layout (location = 0) in vec3 in_coords; layout (location = 1) in vec3 in_color; layout (location = 2) in vec2 in_tex_coords; uniform mat4 transform; out vec3 out_color; out vec2 out_tex_coords; void main() { gl_Position = transform * vec4(in_coords, 1.0); out_color = in_color; out_tex_coords = vec2(in_tex_coords.x, in_tex_coords.y); } """ self.fragment_shader = """ #version 330 core in vec3 out_color; in vec2 out_tex_coords; out vec4 frag_color; uniform sampler2D texture0; uniform sampler2D texture1; void main() { if (out_tex_coords.x > 0.5) frag_color = texture(texture0, out_tex_coords); else frag_color = texture(texture1, out_tex_coords); //frag_color = mix(texture(texture0, out_tex_coords), // texture(texture1, out_tex_coords), 0.5) // * vec4(out_color, 1.0); } """ self.arg_pos = { 'in_coords': 0, 'in_color': 1, 'in_tex_coords': 2, 'out_color': 0 } self.program = QOpenGLShaderProgram(parent=self.parent()) self.program.addCacheableShaderFromSourceCode(QOpenGLShader.Vertex, self.vertex_shader) self.program.addCacheableShaderFromSourceCode(QOpenGLShader.Fragment, self.fragment_shader) self.program.link() self.program.bind() def initialize_geometry(self): self.vertices = np.array( [ # coords color texture coords [0.5, -0.5, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0], # bottom-right [0.5, 0.5, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0], # top-right [-0.5, 0.5, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0], # top-let [-0.5, -0.5, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0] # bottom-let ], dtype=np.float32) self.triangles = np.array([[0, 1, 2], [2, 3, 0]], dtype=np.int32) def initialize_geometry_on_gpu(self): self.vao = QOpenGLVertexArrayObject(self.parent()) if not self.vao.create(): raise ValueError('Could not create VAO') self.vao.bind() self.vbo = QOpenGLBuffer(QOpenGLBuffer.VertexBuffer) if not self.vbo.create(): raise ValueError('Could not create VBO') self.vbo.bind() self.vbo.setUsagePattern(QOpenGLBuffer.StaticDraw) vertices_data = self.vertices.tostring() self.vbo.allocate(len(vertices_data)) self.vbo.write(0, vertices_data, len(vertices_data)) self.ebo = QOpenGLBuffer(QOpenGLBuffer.IndexBuffer) if not self.ebo.create(): raise ValueError('Could not create EBO') self.ebo.bind() self.ebo.setUsagePattern(QOpenGLBuffer.StaticDraw) triangles_data = self.triangles.tostring() self.ebo.allocate(len(triangles_data)) self.ebo.write(0, triangles_data, len(triangles_data)) self.program.enableAttributeArray(self.arg_pos['in_coords']) self.program.setAttributeBuffer( self.arg_pos['in_coords'], gl.GL_FLOAT, 0, 3, self.vertices.shape[1] * self.vertices.dtype.itemsize) self.program.enableAttributeArray(self.arg_pos['in_color']) self.program.setAttributeBuffer( self.arg_pos['in_color'], gl.GL_FLOAT, 3 * self.vertices.dtype.itemsize, 3, self.vertices.shape[1] * self.vertices.dtype.itemsize) self.program.enableAttributeArray(self.arg_pos['in_tex_coords']) self.program.setAttributeBuffer( self.arg_pos['in_tex_coords'], gl.GL_FLOAT, 6 * self.vertices.dtype.itemsize, 2, self.vertices.shape[1] * self.vertices.dtype.itemsize) self.vao.release() self.vbo.release() self.ebo.release() def initialize_texture_on_gpu(self): # Texture 0. image0 = QImage(path.join(DATA_DIR, 'ksmall.jpg')).mirrored() self.texture0 = QOpenGLTexture(image0) self.texture0.setMinificationFilter(QOpenGLTexture.LinearMipMapLinear) self.texture0.setMagnificationFilter(QOpenGLTexture.Linear) self.texture0.setWrapMode(QOpenGLTexture.Repeat) self.texture0.bind(0) self.program.setUniformValue(self.program.uniformLocation('texture0'), 0) # Texture 1. image1 = QImage(path.join(DATA_DIR, 'sunflowerField.jpg')).mirrored() self.texture1 = QOpenGLTexture(image1) self.texture1.setMinificationFilter(QOpenGLTexture.LinearMipMapLinear) self.texture1.setMagnificationFilter(QOpenGLTexture.Linear) self.texture1.setWrapMode(QOpenGLTexture.Repeat) self.texture1.bind(1) self.program.setUniformValue(self.program.uniformLocation('texture1'), 1) def render(self, transform): self.program.bind() self.program.setUniformValue('transform', transform) self.vao.bind() gl.glDrawElements(gl.GL_TRIANGLES, self.triangles.size, gl.GL_UNSIGNED_INT, None) self.program.release()
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 Viewport(View, QOpenGLWidget): def __init__(self, scene, updateFpsDisplay, renderSettings=Rendersettings(), parent=None): View.__init__(self, scene) QOpenGLWidget.__init__(self, parent) self.renderImage = QImage() self.renderSettings = renderSettings self.updateFpsDisplay = updateFpsDisplay self.shaderProgram = QOpenGLShaderProgram() self.viewPortShaderProgram = QOpenGLShaderProgram() self.lightShaderProgram = QOpenGLShaderProgram() self.eyeLoc = QVector3D(0.0, 0.0, 5.0) self.pressLoc = QVector2D() self.isRendering = False self.viewportPlane = ViewportPlane() self.viewportTexture = None self.timer = QTimer() self.timer.setSingleShot(True) self.timer.timeout.connect(self.checkRender) self.scene.registerView(self, [ UpdateType.MATERIAL_CHANGE, UpdateType.MATERIAL_CREATE, UpdateType.MATERIAL_DELETE, UpdateType.OBJECT_CREATE, UpdateType.OBJECT_DELETE, UpdateType.OBJECT_TRANSFORM, UpdateType.CAMERA_CHANGE, UpdateType.LIGHT_CHANGE, UpdateType.SCENE_LOAD ]) self.timestamps = None self.fpsWindow = 10 self.renderer = None self.renderStartTime = None def initializeGL(self): self.glFunctions = self.context().functions() self.glFunctions.glEnable(GL.GL_DEPTH_TEST) self.glFunctions.glEnable(GL.GL_CULL_FACE) self.glFunctions.glClearColor(0.15, 0.25, 0.3, 1.0) vertSuccess = self.shaderProgram.addShaderFromSourceFile( QOpenGLShader.Vertex, "editor/ui/shaders/vertex.vert") fragSuccess = self.shaderProgram.addShaderFromSourceFile( QOpenGLShader.Fragment, "editor/ui/shaders/fragment.frag") print("Vertex shader compilation {}".format( "successful" if vertSuccess else "failed")) print("Fragment shader compilation {}".format( "successful" if fragSuccess else "failed")) print("Program linking {}".format( "successful" if self.shaderProgram.link() else "failed")) vertSuccess = self.viewPortShaderProgram.addShaderFromSourceFile( QOpenGLShader.Vertex, "editor/ui/shaders/viewportvertex.vert") fragSuccess = self.viewPortShaderProgram.addShaderFromSourceFile( QOpenGLShader.Fragment, "editor/ui/shaders/viewportfragment.frag") print("Viewport vertex shader compilation {}".format( "successful" if vertSuccess else "failed")) print("Viewport fragment shader compilation {}".format( "successful" if fragSuccess else "failed")) print("Viewport program linking {}".format( "successful" if self.viewPortShaderProgram.link() else "failed")) vertSuccess = self.lightShaderProgram.addShaderFromSourceFile( QOpenGLShader.Vertex, "editor/ui/shaders/vertex.vert") fragSuccess = self.lightShaderProgram.addShaderFromSourceFile( QOpenGLShader.Fragment, "editor/ui/shaders/lightfragment.frag") print("Light vertex shader compilation {}".format( "successful" if vertSuccess else "failed")) print("Light fragment shader compilation {}".format( "successful" if fragSuccess else "failed")) print("Light program linking {}".format( "successful" if self.viewPortShaderProgram.link() else "failed")) self.shaderProgram.release() self.viewPortShaderProgram.release() # Call all drawable Objects initGL for c in drawableClasses: if isinstance(c, ViewportPlane): c.initGL(self.viewPortShaderProgram) else: c.initGL(self.shaderProgram) def resizeGL(self, w, h): self.scene.getCamera().setAspect(w, h) if self.viewportTexture and self.context().isValid(): self.viewportTexture.destroy() if self.context().isValid(): self.renderImage = QImage(w, h, QImage.Format_RGB32) self.renderImage.fill(Qt.black) self.viewportTexture = QOpenGLTexture(QOpenGLTexture.Target2D) self.viewportTexture.setSize(w, h) self.viewportTexture.setBorderColor(0, 0, 0, 255) self.viewportTexture.setWrapMode(QOpenGLTexture.ClampToBorder) self.viewportTexture.setMinificationFilter(QOpenGLTexture.Nearest) self.viewportTexture.setMagnificationFilter(QOpenGLTexture.Nearest) self.viewportTexture.setData(self.renderImage, QOpenGLTexture.DontGenerateMipMaps) self.updateRenderResolution(w, h) def paintGL(self): self.glFunctions.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT) if not self.isRendering: self.shaderProgram.bind() self.shaderProgram.setUniformValue( "eyePos", self.scene.getCamera().getTranslation()) self.shaderProgram.release() for obj in self.scene.getObjectIterator(): if isinstance(obj, Light): obj.draw(self.lightShaderProgram, self.glFunctions) else: obj.draw(self.shaderProgram, self.glFunctions) else: self.viewportTexture.bind() self.viewportPlane.draw(self.viewPortShaderProgram, self.glFunctions) self.viewportTexture.release() def mousePressEvent(self, e): self.pressLoc = QVector2D(e.localPos()) super().mousePressEvent(e) def mouseMoveEvent(self, e): if e.buttons() == Qt.LeftButton: currPos = QVector2D(e.localPos()) diff = currPos - self.pressLoc diff *= 0.5 self.pressLoc = currPos self.scene.getCamera().rotate(diff.x(), diff.y()) def wheelEvent(self, e): if e.modifiers() == Qt.ShiftModifier: self.scene.getCamera().zoom(-e.angleDelta().y() / 45.0) else: self.scene.getCamera().track(-e.angleDelta().y() / 600.0) def notify(self, updateType): if updateType == UpdateType.SCENE_LOAD: self.scene.getCamera().setAspect(self.width(), self.height()) if self.isRendering: if updateType == UpdateType.CAMERA_CHANGE: self.cameraToRenderer() elif updateType in [ UpdateType.OBJECT_TRANSFORM, UpdateType.OBJECT_CREATE, UpdateType.OBJECT_DELETE, UpdateType.MATERIAL_CREATE, UpdateType.MATERIAL_DELETE, UpdateType.MATERIAL_CHANGE, UpdateType.LIGHT_CHANGE ]: self.renderer.stop() self.renderer.clearMaterials() self.renderer.clearObjects() for mat in self.scene.getMaterialIterator(): self.materialToRenderer(mat) for obj in self.scene.getObjectIterator(): self.sceneObjectToRenderer(obj) self.cameraToRenderer() self.renderer.start() self.timestamps = [[time.time(), 0]] self.renderStartTime = time.time() self.update() def startRender(self): if self.isRendering: return self.isRendering = True self.renderer = PyRenderer(self.width(), self.height()) self.renderer.setRenderSettings(self.renderSettings) self.timestamps = [[time.time(), 0]] self.cameraToRenderer() self.update() for mat in self.scene.getMaterialIterator(): self.materialToRenderer(mat) for obj in self.scene.getObjectIterator(): self.sceneObjectToRenderer(obj) self.renderer.start() self.timer.setInterval(20) self.timer.start() self.renderStartTime = time.time() def stopRender(self): if not self.isRendering: return self.timer.stop() self.isRendering = False self.renderer = None self.resizeGL(self.width(), self.height()) self.update() self.updateFpsDisplay('') def updateRenderResolution(self, width, height): if not self.isRendering: return self.renderer.setResolution(width, height) self.timestamps = [[time.time(), 0]] def checkRender(self): if not self.isRendering: return imageArray, numIterations = self.renderer.getData( self.width(), self.height()) if numIterations < 0 or imageArray.shape != (self.height(), self.width()): self.timer.start() return if self.viewportTexture and self.context().isValid(): self.viewportTexture.destroy() if self.context().isValid(): self.renderImage = QImage(self.width(), self.height(), QImage.Format_RGB32) copyArray = np.ndarray(shape=(self.height(), self.width()), dtype=np.uint32, buffer=self.renderImage.bits()) copyArray[0:, 0:] = imageArray[0:, 0:] self.viewportTexture = QOpenGLTexture(QOpenGLTexture.Target2D) self.viewportTexture.setSize(self.width(), self.height()) self.viewportTexture.setBorderColor(0, 0, 0, 255) self.viewportTexture.setWrapMode(QOpenGLTexture.ClampToBorder) self.viewportTexture.setMinificationFilter(QOpenGLTexture.Nearest) self.viewportTexture.setMagnificationFilter(QOpenGLTexture.Nearest) self.viewportTexture.setData(self.renderImage, QOpenGLTexture.DontGenerateMipMaps) if len(self.timestamps) == self.fpsWindow: self.timestamps.pop() self.timestamps.insert(0, [time.time(), numIterations]) diffs = [(self.timestamps[i][1] - self.timestamps[i + 1][1]) / (self.timestamps[i][0] - self.timestamps[i + 1][0]) for i in range(len(self.timestamps) - 1)] secs = int(time.time() - self.renderStartTime) self.updateFpsDisplay( '{: 4.1f} iterations/sec {:02.0f}:{:02.0f} min {:7d} iterations {:4d} x {:4d} pixel' .format( sum(diffs) / len(diffs), secs // 60, secs % 60, numIterations, self.width(), self.height())) self.update() self.timer.start() def closeViewport(self): self.stopRender() if self.viewportTexture and self.context().isValid(): self.viewportTexture.destroy() def cameraToRenderer(self): cam = self.scene.getCamera() worldPos = cam.getTranslation() targetPos = self.scene.getFocusObject().getTranslation() self.renderer.updateCamera( [worldPos.x(), worldPos.y(), worldPos.z()], [targetPos.x(), targetPos.y(), targetPos.z()], cam.fovY, cam.fStop, cam.focusDistance, cam.stratificationLevel) def sceneObjectToRenderer(self, sceneObject): objType = None materialId = None lightIntensity = 0.0 lightColor = [0.0, 0.0, 0.0] isVisible = True if isinstance(sceneObject, Camera): objType = SceneObjectType.CAMERA materialId = -1 if isinstance(sceneObject, Sphere): objType = SceneObjectType.SPHERE materialId = sceneObject.material.id if isinstance(sceneObject, Cube): objType = SceneObjectType.CUBE materialId = sceneObject.material.id if isinstance(sceneObject, Plane): objType = SceneObjectType.PLANE materialId = sceneObject.material.id if isinstance(sceneObject, Disc): objType = SceneObjectType.DISC materialId = sceneObject.material.id if isinstance(sceneObject, Light): objType = sceneObject.getEmitterShape() materialId = sceneObject.material.id lightIntensity = sceneObject.getIntensity() colVec = sceneObject.getColor() lightColor = [colVec.x(), colVec.y(), colVec.z()] isVisible = sceneObject.getVisibility() matrixArray = sceneObject.calculateModel().transposed().copyDataTo() self.renderer.addObject(sceneObject.id, materialId, objType, matrixArray, lightIntensity, lightColor, sceneObject.area(), isVisible) def materialToRenderer(self, material): if type(material) == DiffuseMaterial: col = material.getEditorColor() self.renderer.addDiffuseMaterial( material.id, [col.x(), col.y(), col.z()]) if type(material) == SpecularMaterial: reflectionColor = material.getReflectionColor() transmissionColor = material.getTransmissionColor() self.renderer.addSpecularMaterial(material.id, [ reflectionColor.x(), reflectionColor.y(), reflectionColor.z() ], [ transmissionColor.x(), transmissionColor.y(), transmissionColor.z() ], material.getIOR()) def setRenderSettings(self, renderSettings): self.renderSettings = renderSettings if self.isRendering: self.renderer.setRenderSettings(self.renderSettings) def getRenderSettings(self): return self.renderSettings def saveImage(self): if not self.isRendering: return saveImage = self.renderImage.mirrored(horizontal=True) fileTuple = QFileDialog.getSaveFileName( self, 'Save destination', dir="./images", filter='Images (*.png *.jpg);;All files (*)') if fileTuple[0]: filename = fileTuple[0] if '.' not in filename: filename += '.png' saveImage.save(filename, quality=100)
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
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 GLWidget(QOpenGLWidget, QOpenGLFunctions): xRotationChanged = Signal(int) yRotationChanged = Signal(int) zRotationChanged = Signal(int) zoomChanged = Signal(int) fileSaved = Signal() def __init__(self, parent=None): QOpenGLWidget.__init__(self, parent) QOpenGLFunctions.__init__(self) self.core = "--coreprofile" in QCoreApplication.arguments() self.xRot = 0 self.yRot = 0 self.zRot = 4327 self.zoom = 0 self.lastPos = 0 self.lithophane = Lithophane() self.vao = QOpenGLVertexArrayObject() self.lithophaneVbo = QOpenGLBuffer() self.program = QOpenGLShaderProgram() self.projMatrixLoc = 0 self.mvMatrixLoc = 0 self.normalMatrixLoc = 0 self.lightPosLoc = 0 self.proj = QMatrix4x4() self.camera = QMatrix4x4() self.world = QMatrix4x4() self.transparent = "--transparent" in QCoreApplication.arguments() if self.transparent: fmt = self.format() fmt.setAlphaBufferSize(8) self.setFormat(fmt) def updateImage(self, img_scr): self.fileDir = img_scr self.lithophane.generateVertex(img_scr) self.initializeGL() def generateSTL(self,file_name): file=open(file_name, "w") file.write("solid lithophane\n") i=0 v_data=[] while(i <= self.lithophane.m_count): n1=self.lithophane.m_data[i] i+=1 n2=self.lithophane.m_data[i] i+=1 n3=self.lithophane.m_data[i] i+=1 v1=self.lithophane.m_data[i] i+=1 v2=self.lithophane.m_data[i] i+=1 v3=self.lithophane.m_data[i] i+=1 v_data.append([n1,n2,n3]) triangle_count = 0 for i in range(0,len(v_data)-1,4): q1 = v_data[i] q2 = v_data[i+1] q3 = v_data[i+2] q4 = v_data[i+3] file.write("facet normal 0.0 0.0 0.0\n") file.write(" outer loop\n") file.write(" vertex {} {} {}\n".format(np.float32(q1[0]),np.float32(q1[1]),np.float32(q1[2]))) file.write(" vertex {} {} {}\n".format(np.float32(q2[0]),np.float32(q2[1]),np.float32(q2[2]))) file.write(" vertex {} {} {}\n".format(np.float32(q4[0]),np.float32(q4[1]),np.float32(q4[2]))) file.write(" end loop\n") file.write("endfacet\n") file.write("facet normal 0.0 0.0 0.0\n") file.write(" outer loop\n") file.write(" vertex {} {} {}\n".format(np.float32(q2[0]),np.float32(q2[1]),np.float32(q2[2]))) file.write(" vertex {} {} {}\n".format(np.float32(q3[0]),np.float32(q3[1]),np.float32(q3[2]))) file.write(" vertex {} {} {}\n".format(np.float32(q4[0]),np.float32(q4[1]),np.float32(q4[2]))) file.write(" end loop\n") file.write("endfacet\n") triangle_count+=2 print("Written {} triangles".format(triangle_count)) file.close() self.fileSaved.emit() def xRotation(self): return self.xRot def yRotation(self): return self.yRot def zRotation(self): return self.zRot def minimumSizeHint(self): return QSize(50, 50) def sizeHint(self): return QSize(400, 400) def normalizeAngle(self, angle): while angle < 0: angle += 360 * 16 while angle > 360 * 16: angle -= 360 * 16 return angle def setXRotation(self, angle): angle = self.normalizeAngle(angle) if angle != self.xRot: self.xRot = angle self.emit(SIGNAL("xRotationChanged(int)"), angle) self.update() def setYRotation(self, angle): angle = self.normalizeAngle(angle) if angle != self.yRot: self.yRot = angle self.emit(SIGNAL("yRotationChanged(int)"), angle) self.update() def setZRotation(self, angle): angle = self.normalizeAngle(angle) if angle != self.zRot: self.zRot = angle self.emit(SIGNAL("zRotationChanged(int)"), angle) print(angle) self.update() def setZoom(self, val): if val != self.zoom: self.zoom = -val self.emit(SIGNAL("zoomChanged(int)"), val) self.update() def applyParams(self, minT, maxT, step_size): if not self.fileDir: return self.lithophane.setParams(minT,maxT,step_size) self.updateImage(self.fileDir) def cleanup(self): self.makeCurrent() self.lithophaneVbo.destroy() del self.program self.program = None self.doneCurrent() def vertexShaderSourceCore(self): return """#version 150 in vec4 vertex; in vec3 normal; out vec3 vert; out vec3 vertNormal; uniform mat4 projMatrix; uniform mat4 mvMatrix; uniform mat3 normalMatrix; void main() { vert = vertex.xyz; vertNormal = normalMatrix * normal; gl_Position = projMatrix * mvMatrix * vertex; }""" def fragmentShaderSourceCore(self): return """#version 150 in highp vec3 vert; in highp vec3 vertNormal; out highp vec4 fragColor; uniform highp vec3 lightPos; void main() { highp vec3 L = normalize(lightPos - vert); highp float NL = max(dot(normalize(vertNormal), L), 0.0); highp vec3 color = vec3(0.39, 1.0, 0.0); highp vec3 col = clamp(color * 0.2 + color * 0.8 * NL, 0.0, 1.0); fragColor = vec4(col, 1.0); }""" def vertexShaderSource(self): return """attribute vec4 vertex; attribute vec3 normal; varying vec3 vert; varying vec3 vertNormal; uniform mat4 projMatrix; uniform mat4 mvMatrix; uniform mat3 normalMatrix; void main() { vert = vertex.xyz; vertNormal = normalMatrix * normal; gl_Position = projMatrix * mvMatrix * vertex; }""" def fragmentShaderSource(self): return """varying highp vec3 vert; varying highp vec3 vertNormal; uniform highp vec3 lightPos; void main() { highp vec3 L = normalize(lightPos - vert); highp float NL = max(dot(normalize(vertNormal), L), 0.0); highp vec3 color = vec3(0.39, 1.0, 0.0); highp vec3 col = clamp(color * 0.2 + color * 0.8 * NL, 0.0, 1.0); gl_FragColor = vec4(col, 1); }""" def initializeGL(self): self.context().aboutToBeDestroyed.connect(self.cleanup) self.initializeOpenGLFunctions() self.glClearColor(0, 0, 0, 1) self.program = QOpenGLShaderProgram() if self.core: self.vertexShader = self.vertexShaderSourceCore() self.fragmentShader = self.fragmentShaderSourceCore() else: self.vertexShader = self.vertexShaderSource() self.fragmentShader = self.fragmentShaderSource() self.program.addShaderFromSourceCode(QOpenGLShader.Vertex, self.vertexShader) self.program.addShaderFromSourceCode(QOpenGLShader.Fragment, self.fragmentShader) self.program.bindAttributeLocation("vertex", 0) self.program.bindAttributeLocation("normal", 1) self.program.link() self.program.bind() self.projMatrixLoc = self.program.uniformLocation("projMatrix") self.mvMatrixLoc = self.program.uniformLocation("mvMatrix") self.normalMatrixLoc = self.program.uniformLocation("normalMatrix") self.lightPosLoc = self.program.uniformLocation("lightPos") self.vao.create() vaoBinder = QOpenGLVertexArrayObject.Binder(self.vao) self.lithophaneVbo.create() self.lithophaneVbo.bind() float_size = ctypes.sizeof(ctypes.c_float) self.lithophaneVbo.allocate(self.lithophane.constData(), 300000 *6 * float_size) self.setupVertexAttribs() self.camera.setToIdentity() self.camera.translate(0, 0, -90) self.program.setUniformValue(self.lightPosLoc, QVector3D(10, 0, 20)) self.program.release() vaoBinder = None def setupVertexAttribs(self): self.lithophaneVbo.bind() f = QOpenGLContext.currentContext().functions() f.glEnableVertexAttribArray(0) f.glEnableVertexAttribArray(1) float_size = ctypes.sizeof(ctypes.c_float) null = VoidPtr(0) pointer = VoidPtr(3 * float_size) f.glVertexAttribPointer(0, 3, int(GL.GL_FLOAT), int(GL.GL_FALSE), 6 * float_size, null) f.glVertexAttribPointer(1, 3, int(GL.GL_FLOAT), int(GL.GL_FALSE), 6 * float_size, pointer) self.lithophaneVbo.release() def paintGL(self): self.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT) self.glEnable(GL.GL_DEPTH_TEST) self.glEnable(GL.GL_CULL_FACE) self.world.setToIdentity() self.world.rotate(self.xRot / 16, 1, 0, 0) self.world.rotate(self.yRot / 16, 0, 1, 0) self.world.rotate(self.zRot / 16, 0, 0, 1) self.camera.setToIdentity() self.camera.translate(0,0,self.zoom) self.world.translate(-self.lithophane.width*0.5*0.2,-self.lithophane.height*0.5*0.2 , 0) vaoBinder = QOpenGLVertexArrayObject.Binder(self.vao) self.program.bind() self.program.setUniformValue(self.projMatrixLoc, self.proj) self.program.setUniformValue(self.mvMatrixLoc, self.camera * self.world) normalMatrix = self.world.normalMatrix() self.program.setUniformValue(self.normalMatrixLoc, normalMatrix) self.glDrawArrays(GL.GL_QUADS, 0, self.lithophane.vertexCount()) self.program.release() vaoBinder = None def resizeGL(self, width, height): self.proj.setToIdentity() self.proj.perspective(45, width / height, 0.01, 1000) def mousePressEvent(self, event): self.lastPos = QPoint(event.pos()) def mouseMoveEvent(self, event): dx = event.x() - self.lastPos.x() dy = event.y() - self.lastPos.y() if event.buttons() & Qt.LeftButton: self.setXRotation(self.xRot + 8 * dy) self.setYRotation(self.yRot + 8 * dx) elif event.buttons() & Qt.RightButton: self.setXRotation(self.xRot + 8 * dy) self.setZRotation(self.zRot + 8 * dx) self.lastPos = QPoint(event.pos())
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()