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 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 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 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 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 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() 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; out vec3 out_color; void main() { gl_Position = vec4(in_coords, 1.0); gl_PointSize = 200.0; out_color = in_color; } """ self.fragment_shader = """ #version 330 core in vec3 out_color; out vec4 frag_color; void main() { frag_color = vec4(out_color, 1.0); } """ self.arg_pos = {'in_coords': 0, 'in_color': 1, '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() def initialize_geometry(self): self.vertices = np.array( [ # coords color [0.5, 0.5, 0.0, 1.0, 0.0, 0.0], # top-right [0.5, -0.5, 0.0, 0.0, 1.0, 0.0], # bottom-right [-0.5, -0.5, 0.0, 0.0, 0.0, 1.0], # bottom-left [-0.5, 0.5, 0.0, 1.0, 1.0, 0.0] # top-left ], 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.vao.release() self.vbo.release() self.ebo.release() def render(self): self.program.bind() self.vao.bind() gl.glDrawElements(gl.GL_TRIANGLES, self.triangles.size, gl.GL_UNSIGNED_INT, None) self.program.release()