Кто-нибудь может привести пример использования методов OpenGL PyQt5? - PullRequest
0 голосов
/ 07 апреля 2020

Как правильно использовать setAttributeBuffer ? В чем должно измеряться смещение? Б? или количество элементов? Ни один из них не работает при использовании нескольких атрибутов вершин. Что именно обеспечить аргументу индексов в glDrawElements? Почему я получаю 0 от QOpenGLShaderProgram.attributeLocation? Как получить атрибут местоположения из шейдера? Я ссылаюсь на пример кода, который должен нарисовать сетку. Без использования атрибута colour это работает. Но как только я включаю атрибут цвета, я ничего не получаю на экране. Может кто-нибудь указать, что не так или хотя бы привести пример использования? Вызовы setAttributeBuffer можно найти в GraphicsObject.allocateVertices()

# PyQT/OpenGL example
import ctypes
import os
import time
from functools import partial
from math import copysign

import numpy
from PyQt5 import QtGui, QtWidgets, QtCore

VPORTMIN = -8.0
VPORTMAX = 8.0
DAMPENER = 0.1


SHADERPATH = './'


def parseShader(file: str, program: QtGui.QOpenGLShaderProgram):
    def readStrings(inFile: file):
        source = ''
        lines = []
        lastpos = inFile.tell()
        for data in iter(inFile.readline, ''):
            if "#shader" in data:
                break
            elif data == eof:
                break
            lines.append(data)
            lastpos = inFile.tell()

        inFile.seek(lastpos)
        return source.join(lines)

    file = open(file, 'r')
    file.readlines()
    eof = file.tell()  # get location of EOF character.
    file.seek(0, 0)  # go back to file beginning.
    vs = ''
    fs = ''
    while True:
        line = file.readline()
        if file.tell() == eof:
            # Reached EOF.
            break
        elif not line:
            continue
        elif line == "#shader vertex\n":
            vs = readStrings(file)
        elif line == "#shader fragment\n":
            fs = readStrings(file)
    program.addShaderFromSourceCode(QtGui.QOpenGLShader.Vertex, vs)
    program.addShaderFromSourceCode(QtGui.QOpenGLShader.Fragment, fs)
    if not program.link():
        log = program.log()
        print(log)
        return False
    else:
        return True


class Window(QtWidgets.QMainWindow):
    def __init__(self, app):
        super().__init__()

        self.app = app
        self.app.mainwindow = self
        self.glviewPort = GLViewport(parent=self)
        self.glviewPort.setMinimumSize(600, 600)
        self.glviewPort.setMouseTracking(True)
        self.glviewPort.grabKeyboard()
        self.setCentralWidget(self.glviewPort)

    def closeEvent(self, event: QtGui.QCloseEvent):
        self.glviewPort.flush()
        return super().closeEvent(event)


class GraphicsObject:
    def __init__(self, name=None):
        self.name = name
        self.vao = QtGui.QOpenGLVertexArrayObject()
        self.vbo = QtGui.QOpenGLBuffer(QtGui.QOpenGLBuffer.VertexBuffer)
        self.ibo = QtGui.QOpenGLBuffer(QtGui.QOpenGLBuffer.IndexBuffer)
        self.vertices = []
        self.indices = []
        self.dtype = None
        self.modelMatrix = QtGui.QMatrix4x4()
        self.modelMatrix.setToIdentity()
        self.viewMatrix = QtGui.QMatrix4x4()
        self.viewMatrix.setToIdentity()
        self.projMatrix = QtGui.QMatrix4x4()
        self.mvpMatrix = self.projMatrix * self.viewMatrix * self.modelMatrix
        self.shaderProgram = None
        self.vertexPosIdx = 0
        self.vertexColorIdx = 0
        self.u_MVPidx = 0
        self.primType = None
        self.count = 0
        self.color = QtGui.QVector4D(1.0, 1.0, 1.0, 0.0)
        self.rendererId = None
        self.blendStatus = False
        self.lineSmoothStat = False
        self.linewidth = 1.0
        self.usagePattern = QtGui.QOpenGLBuffer.StaticDraw
        self.dirty = False
        self.allowzoom = True

    def createObjects(self):
        self.shaderProgram = QtGui.QOpenGLShaderProgram()
        self.shaderProgram.create()
        self.vao.create()
        self.vbo.create()
        self.ibo.create()

    def buildShader(self):
        if not parseShader(os.path.join(SHADERPATH, 'graphshader.glsl'), self.shaderProgram):
            print("Failed to compile shader!")
            sys.exit(1)
        else:
            self.shaderProgram.bind()
            self._cacheUniforms()

    def _cacheUniforms(self):
        self.vertexPosIdx = self.shaderProgram.attributeLocation("position")
        self.vertexColorIdx = self.shaderProgram.attributeLocation("color")
        self.u_MVPidx = self.shaderProgram.uniformLocation("u_mvp")
        print(self.u_MVPidx)

    def bindAll(self):
        self.shaderProgram.bind()
        self.vao.bind()
        self.vbo.bind()
        self.ibo.bind()

    def setDatatype(self, dtype):
        """
        Sets datatype.
        Args:
            dtype: dtype.

        Returns: None

        """
        self.dtype = dtype

    def setUsagePattern(self, pattern):
        self.usagePattern = pattern

    def allocateVertices(self, vertices: list):
        self.vbo.setUsagePattern(self.usagePattern)
        self.ibo.setUsagePattern(self.usagePattern)
        self.vertices = numpy.array(vertices, dtype=numpy.float32)
        if self.vbo.bufferId():
            self.vbo.allocate(self.vertices.ctypes.data_as(ctypes.POINTER(ctypes.c_void_p)).contents,
                              sys.getsizeof(self.vertices))
            self.shaderProgram.enableAttributeArray(self.vertexPosIdx)
            self.shaderProgram.setAttributeBuffer(self.vertexPosIdx,  # Location of attribute in vertex shader.
                                                  self.dtype,  # data type of vertices.
                                                  0,  # Start location in vertices buffer.
                                                  3,  # No. of components per vertex in vertices buffer.
                                                  7 * 4)  # stride, number of bytes b/w consecutive vertices.
            self.shaderProgram.enableAttributeArray(self.vertexColorIdx)
            self.shaderProgram.setAttributeBuffer(self.vertexColorIdx,  # Location of attribute in vertex shader.
                                                 self.dtype,  # data type of vertices.
                                                 3 * 4,  # Start location to color attributes in vertices buffer.
                                                 4,  # No. of components per vertex in vertices buffer.
                                                 7 * 4)  # stride, number of bytes b/w consecutive vertices.

        self.dirty = True

    def allocateIndices(self, indices):
        self.ibo.bind()
        self.indices = numpy.array(indices, dtype=numpy.uint32)
        if self.ibo.bufferId():
            self.ibo.allocate(self.indices.ctypes.data_as(ctypes.POINTER(ctypes.c_void_p)).contents,
                              sys.getsizeof(self.indices))

    def unbindAll(self):
        self.shaderProgram.release()
        self.vao.release()
        self.vbo.release()

    def destroyBuffers(self):
        self.vao.destroy()
        self.vbo.destroy()
        self.ibo.destroy()

    def destroy(self):
        self.unbindAll()
        self.destroyShader()
        self.destroyBuffers()

    def destroyShader(self):
        self.shaderProgram.removeAllShaders()

    def setAllowZoom(self, state):
        """
        Allow/block camera zoom.
        Args:
            state: bool

        Returns: None

        """
        self.allowzoom = state

    def setPrimitives(self, primType):
        self.primType = primType

    def setCount(self, count):
        self.count = count

    def setColor(self, r, g, b, a):
        """
        Sets color
        Args:
            r: red 0-1
            g: green 0-1
            b: blue 0-1
            a: alpha 0-1

        Returns:

        """
        self.color = QtGui.QVector4D(r, g, b, a)

    def setBlend(self, status):
        """
        Enables blend bit.
        Args:
            status: bool

        Returns: None

        """
        self.blendStatus = status

    def setLineSmooth(self, status):
        """
        Enables line smooth.
        Args:
            status: bool

        Returns: None

        """
        self.lineSmoothStat = status

    def setLineWidth(self, w: float):
        """
        Sets a line width.
        Args:
            w: float; clamped to 0.1-1

        Returns: None

        """
        self.linewidth = w
        if copysign(1, w - 1.0) > 0:
            # provided width > 1
            self.linewidth = w
        elif copysign(1, w - 0.0) < 0:
            # provided width < 0
            self.linewidth = 0.1

    def setTranslation(self, x, y, z):
        """
        Translates model by x, y, z.
        Args:
            x: translate along x. in units of dx
            y: translate along y. in units of dy
            z: translate along z. in units of dz

        Returns: None

        """
        self.modelMatrix.translate(QtGui.QVector3D(x, y, z))
        self.mvpMatrix = self.projMatrix * self.viewMatrix * self.modelMatrix

    def setScale(self, sx=1.0, sy=1.0, sz=1.0):
        """
        Scales model by sx, sy, sz. Default is 1.0, i.e, no scale.
        Args:
            sx: scale along x
            sy: scale along y
            sz: scale along z

        Returns: None

        """
        self.modelMatrix.scale(QtGui.QVector3D(sx, sy, sz))
        self.mvpMatrix = self.projMatrix * self.viewMatrix * self.modelMatrix

    def setRotation(self, angle, x=0.0, y=0.0, z=1.0):
        """
        Rotates model by angle degrees around axis (x, y, z). Default axis points out of screen. z+ve.
        angle > 0, CW rotation
        angle < 0, CCW rotation
        Args:
            angle: degrees
            x: axis direction
            y: axis direction
            z: axis direction

        Returns: None

        """
        self.modelMatrix.rotate(angle, QtGui.QVector3D(x, y, z))
        self.mvpMatrix = self.projMatrix * self.viewMatrix * self.modelMatrix

    def setViewMat(self, mat: QtGui.QMatrix4x4):
        if not self.allowzoom:
            data = mat.data()
            data[0] = 1.0
            data[5] = 1.0
            data[10] = 1.0
            self.viewMatrix = QtGui.QMatrix4x4(data).transposed()
        else:
            self.viewMatrix = mat
            self.mvpMatrix = self.projMatrix * self.viewMatrix * self.modelMatrix

    def setProjMat(self, mat: QtGui.QMatrix4x4):
        self.projMatrix = mat
        self.mvpMatrix = self.projMatrix * self.viewMatrix * self.modelMatrix

    def drawCall(self, gl):
        if self.blendStatus:
            gl.glEnable(gl.GL_BLEND)
            gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_DST_ALPHA)
        if self.lineSmoothStat:
            gl.glEnable(gl.GL_LINE_SMOOTH)
        if 1.0 - self.linewidth > 1.0e-5:
            gl.glLineWidth(self.linewidth)
        self.shaderProgram.bind()
        self.vao.bind()
        self.ibo.bind()
        self.shaderProgram.setUniformValue(self.u_MVPidx, self.mvpMatrix)
        gl.glDrawElements(self.primType, len(self.indices), gl.GL_UNSIGNED_INT, None)
        self.shaderProgram.release()
        self.vao.release()
        self.ibo.release()
        if self.blendStatus:
            gl.glDisable(gl.GL_BLEND)
        if self.lineSmoothStat:
            gl.glDisable(gl.GL_LINE_SMOOTH)
        if 1.0 - self.linewidth > 1.0e-5:
            gl.glLineWidth(1.0)

    def updateContents(self, offset, data: list, count):
        self.bindAll()
        data = numpy.array(data, dtype=numpy.float32)
        self.vbo.write(offset, data.ctypes.data_as(ctypes.POINTER(ctypes.c_void_p)).contents, count)
        self.unbindAll()
        self.dirty = True

    def resetTransform(self):
        self.modelMatrix.setToIdentity()
        self.mvpMatrix = self.projMatrix * self.viewMatrix * self.modelMatrix


class Camera:
    """
    An abstract camera class that processes input and calculates the vectors and matrices for use in OpenGL.
    """
    UP = QtCore.Qt.Key_Up
    DOWN = QtCore.Qt.Key_Down
    LEFT = QtCore.Qt.Key_Left
    RIGHT = QtCore.Qt.Key_Right
    movementSpeed = 2.5
    xmin = VPORTMIN
    xmax = VPORTMAX
    ymin = VPORTMIN
    ymax = VPORTMAX
    lastPos = QtGui.QVector3D()
    lowerleft = QtCore.QPointF()
    upperright = QtCore.QPointF()

    def __init__(self, position: QtGui.QVector3D, front: QtGui.QVector3D, up: QtGui.QVector3D):
        """
        Constructor. Initialize a camera at origin, looking at center with up as specified.
        Args:
            position: position of camera in world space, i.e, the space with all other objects.
            front: the camera looks in the front direction.
            up: the camera when upright has it's up axis pointed in specified 'up' direction.
        """
        self.position = position
        self.front = front
        self.up = up
        self.position = position
        self.direction = front
        self.up = up
        self.right = QtGui.QVector3D.crossProduct(self.direction, self.up)
        self.front = self.position + self.direction
        self.view = QtGui.QMatrix4x4()
        self.zoomIncrement = 0.0
        self.view.lookAt(self.position, self.front, self.up)
        self.projection = QtGui.QMatrix4x4()
        self.projection.ortho(VPORTMIN, VPORTMAX, VPORTMIN, VPORTMAX, 0.0, 100.0)

    def getViewMatrix(self):
        """
        Return a view matrix
        Returns: QtGui.QMatrix4x4

        """
        return self.view

    def getProjMatrix(self):
        """
        Return a projection matrix
        Returns: QtGui.QMatrix4x4

        """
        return self.projection

    def processMousePan(self, posNdc: QtGui.QVector3D, eventType: QtGui.QMouseEvent.Type):
        """
        Pan the viewport in direction of mouse move.
        Args
            pos (QtGui.QVector3D): in viewport co-ordinates.
            eventType (QtGui.QMouseEvent.Type): type of mouse event.

        Returns: None

        """
        posProj = self.projection.inverted()[0] * posNdc
        posMV = self.view.inverted()[0] * posProj
        if eventType == QtGui.QMouseEvent.MouseButtonPress:
            self.lastPos = posMV
        elif eventType == QtGui.QMouseEvent.MouseMove:
            offset = posMV - self.lastPos
            offset.setZ(0.0)
            self.view.translate(offset)
            self.position = self.position - offset
            self.front = self.position + self.direction
            self.view.setToIdentity()
            self.view.lookAt(self.position, self.front, self.up)
        elif eventType == QtGui.QMouseEvent.MouseButtonRelease:
            pass

    def processKbd(self, direction, deltatime):
        """
        Process up,down,left,right key inputs.
        Returns: None

        """
        velocity = self.movementSpeed * deltatime
        if direction == self.UP:
            # Move everything down
            dx = QtGui.QVector3D(0.0, 0.0, 0.0)
            dy = QtGui.QVector3D(0.0, velocity, 0.0)
        elif direction == self.DOWN:
            # Move everything up
            dx = QtGui.QVector3D(0.0, 0.0, 0.0)
            dy = QtGui.QVector3D(0.0, -velocity, 0.0)
        elif direction == self.RIGHT:
            # Move everything left
            dx = QtGui.QVector3D(velocity, 0.0, 0.0)
            dy = QtGui.QVector3D(0.0, 0.0, 0.0)
        elif direction == self.LEFT:
            # Move everything right
            dx = QtGui.QVector3D(-velocity, 0.0, 0.0)
            dy = QtGui.QVector3D(0.0, 0.0, 0.0)
        else:
            return
        self.position = self.position + dx + dy
        self.front = self.position + self.direction
        self.view.setToIdentity()
        self.view.lookAt(self.position, self.front, self.up)

    def fitView(self, xmin, xmax, ymin, ymax):

        self.projection.setToIdentity()
        self.projection.ortho(xmin, xmax, ymin, ymax, -0.1, 100.0)

    def processResize(self, w: float, h: float, keepAspectRatio=True):
        if keepAspectRatio:
            aspectRatio = w / h
            xSpan = 1.0
            ySpan = 1.0
            if aspectRatio >= 1:
                # width >= height
                xSpan *= aspectRatio
            else:
                # height > width
                ySpan = xSpan / aspectRatio
            self.xmin = VPORTMIN * xSpan
            self.xmax = VPORTMAX * xSpan
            self.ymin = VPORTMIN * ySpan
            self.ymax = VPORTMAX * ySpan
        self.fitView(self.xmin, self.xmax, self.ymin, self.ymax)

    def processWheel(self, factor, w, h, deltatime):
        aspectRatio = float(w) / float(h)
        xSpan = 1.0
        ySpan = 1.0
        if aspectRatio >= 1:
            # width >= height
            xSpan *= aspectRatio
        else:
            # height > width
            ySpan = xSpan / aspectRatio
        zDampener = min(self.xmax - self.xmin, self.ymax - self.ymin)
        # Damp zoom so that it's slow when far away. Also consider frame latency.
        # exponential function from [https://www.geogebra.org/m/eBHzJyKt]
        zDampener = 0.8 * deltatime * 1.00 ** zDampener
        if copysign(1, (factor - 1.0)) < 0:
            # zoom out
            self.xmin -= zDampener * xSpan
            self.xmax += zDampener * xSpan
            self.ymin -= zDampener * ySpan
            self.ymax += zDampener * ySpan
        else:
            # zoom in
            self.xmin += zDampener * xSpan
            self.xmax -= zDampener * xSpan
            self.ymin += zDampener * ySpan
            self.ymax -= zDampener * ySpan
        if (self.xmin > -1.0) or (self.ymin > -1.0):
            # zoom out
            self.xmin -= zDampener * xSpan
            self.xmax += zDampener * xSpan
            self.ymin -= zDampener * ySpan
            self.ymax += zDampener * ySpan
            self.fitView(self.xmin, self.xmax, self.ymin, self.ymax)
        else:
            self.fitView(self.xmin, self.xmax, self.ymin, self.ymax)


class Renderer:
    """
    Manages state of graphics objects. This is responsible for drawing on viewport.
    """

    def __init__(self, w, h):
        self.w = w
        self.h = h
        self.objects = set()

    def register(self, obj: GraphicsObject):
        self.objects.add(obj)
        obj.rendererId = self.__hash__()

    def deRegister(self, obj: GraphicsObject):
        self.objects.discard(obj)
        obj.rendererId = None

    def draw(self, gl, camera):
        """
        Call draw calls of all registered objects.
        Returns: None

        """
        gl.glEnable(gl.GL_DEPTH_TEST)
        gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
        for obj in self.objects:
            if obj.rendererId is None:
                continue
            else:
                obj.setViewMat(camera.getViewMatrix())
                obj.setProjMat(camera.getProjMatrix())
                obj.drawCall(gl)


class GLViewport(QtWidgets.QOpenGLWidget):
    def __init__(self, parent=None):
        """
        Re-implements QOpenGLWidget.
        Args:
            parent: Optional.
        """
        super().__init__(parent)
        self.camera = Camera(QtGui.QVector3D(0.0, 0.0, 100.0), QtGui.QVector3D(0.0, 0.0, -1.0),
                             QtGui.QVector3D(0.0, 1.0, 0.0))
        self.renderer = Renderer(self.width(), self.height())
        self.axGrid = GraphicsObject("axGrid")
        self.m_gl = None
        self.m_time = 0
        self.m_deltatime = 0
        self.m_frameCount = 0
        self.numScheduledScalings = 0

    def initializeGL(self):
        defaultFmt = QtGui.QSurfaceFormat.defaultFormat()
        verProf = QtGui.QOpenGLVersionProfile(defaultFmt)
        self.m_gl = self.context().versionFunctions(verProf)
        self.m_gl.initializeOpenGLFunctions()
        self.m_gl.glClearColor(0.224, 0.224, 0.224, 1.0)

        # Co-ordinate lines.
        vertices = []
        indices = []
        delta = 1.0
        xmin = -5.0
        xmax = 5.0
        dx = delta
        ymin = -5.0
        ymax = 5.0
        dy = delta
        nHlines = int((xmax - xmin) / dx) + 1
        nVlines = int((ymax - ymin) / dy) + 1
        r = 0.4
        g = 0.4
        b = 0.4
        a = 0.302
        # Horizontal lines
        for i in range(0, nHlines):
            y = ymin + i * dy
            p1 = [xmin, y, 1.0]
            p2 = [xmax, y, 1.0]
            vertices.append(p1[0])
            vertices.append(p1[1])
            vertices.append(p1[2])
            vertices.append(r)
            vertices.append(g)
            vertices.append(b)
            vertices.append(a)
            vertices.append(p2[0])
            vertices.append(p2[1])
            vertices.append(p2[2])
            vertices.append(r)
            vertices.append(g)
            vertices.append(b)
            vertices.append(a)
        # Vertical lines
        for i in range(0, nVlines):
            x = xmin + i * dx
            p1 = [x, ymin, 1.0]
            p2 = [x, ymax, 1.0]
            vertices.append(p1[0])
            vertices.append(p1[1])
            vertices.append(p1[2])
            vertices.append(r)
            vertices.append(g)
            vertices.append(b)
            vertices.append(a)
            vertices.append(p2[0])
            vertices.append(p2[1])
            vertices.append(p2[2])
            vertices.append(r)
            vertices.append(g)
            vertices.append(b)
            vertices.append(a)
        for i in range(2 * nHlines + 2 * nVlines):
            indices.append(i)
        self.axGrid.createObjects()
        self.axGrid.buildShader()
        self.axGrid.bindAll()
        self.axGrid.setCount(len(vertices) / 3)
        self.axGrid.setDatatype(self.m_gl.GL_FLOAT)
        self.axGrid.setUsagePattern(QtGui.QOpenGLBuffer.StaticDraw)
        self.axGrid.allocateVertices(vertices)
        self.axGrid.allocateIndices(indices)
        self.axGrid.unbindAll()
        self.axGrid.setPrimitives(self.m_gl.GL_LINES)
        self.axGrid.setBlend(True)

        # Register graphics objects.
        self.renderer.register(self.axGrid)

        self.getGLinfo()

    def getGLinfo(self):
        profile = "None"
        if self.context().format().profile() == QtGui.QSurfaceFormat.CoreProfile:
            profile = "Core"
        elif self.context().format().profile() == QtGui.QSurfaceFormat.CompatibilityProfile:
            profile = "Compat"
        print("""
        OpenGL : {0}
        OpenGLES : {1}
        Version : {2}
        Major Version : {3}
        Minor Version : {4}
        Profile : {5}
        versionFunctions : {6}
            """.format(not self.context().isOpenGLES(), self.context().isOpenGLES(),
                       self.m_gl.glGetString(self.m_gl.GL_VERSION),
                       self.m_gl.glGetIntegerv(self.m_gl.GL_MAJOR_VERSION),
                       self.m_gl.glGetIntegerv(self.m_gl.GL_MINOR_VERSION),
                       profile, self.m_gl))

    def resizeGL(self, w: int, h: int):
        self.m_gl.glViewport(0, 0, w, h)
        # Re-adjust ortho
        self.camera.processResize(float(w), float(h), keepAspectRatio=True)
        self.update()

    def paintGL(self):
        if self.m_frameCount == 0:
            self.m_time = time.time()
        self.m_gl.glViewport(0, 0, self.width(), self.height())
        self.renderer.draw(self.m_gl, self.camera)
        self.m_deltatime = time.time() - self.m_time
        try:
            self.fps = 1. / (self.m_deltatime)
            # print(self.fps)
        except ZeroDivisionError:
            pass
        self.m_frameCount += 1
        self.m_time = time.time()

    def mousePressEvent(self, event: QtGui.QMouseEvent):
        """
        Allow pan with MMB, selection with LMB, context menu with RMB
        Args:
            event: the event.

        Returns: None

        """
        button = event.button()
        modifier = event.modifiers()
        posViewport = event.pos()
        posNdc = QtGui.QVector3D()
        posNdc.setX((2. / self.width()) * (posViewport.x() - (self.width() / 2.)))
        posNdc.setY((2. / self.height()) * (-posViewport.y() + (self.height() / 2.)))  # note the flip in sign.
        posNdc.setZ(0.0)
        if button == QtCore.Qt.MiddleButton:
            self.camera.processMousePan(posNdc, event.type())

        self.update()
        return super().mousePressEvent(event)

    def mouseMoveEvent(self, event: QtGui.QMouseEvent):
        """
        Allow pan with MMB, selection with LMB, context menu with RMB
        Args:
            event: the event.

        Returns: None

        """
        buttons = event.buttons()
        modifier = event.modifiers()
        posViewport = event.pos()
        posNdc = QtGui.QVector3D()
        posNdc.setX((2. / self.width()) * (posViewport.x() - (self.width() / 2.)))
        posNdc.setY((2. / self.height()) * (-posViewport.y() + (self.height() / 2.)))  # note the flip in sign.
        posNdc.setZ(0.0)
        if buttons == QtCore.Qt.MiddleButton:
            self.camera.processMousePan(posNdc, event.type())

        self.update()

        return super().mouseMoveEvent(event)

    def mouseReleaseEvent(self, event: QtGui.QMouseEvent):
        """
        Allow pan with MMB, selection with LMB, context menu with RMB
        Args:
            event: the event.

        Returns: None

        """
        button = event.button()
        modifier = event.modifiers()
        posViewport = event.pos()
        posNdc = QtGui.QVector3D()
        posNdc.setX((2. / self.width()) * (posViewport.x() - (self.width() / 2.)))
        posNdc.setY((2. / self.height()) * (-posViewport.y() + (self.height() / 2.)))  # note the flip in sign.
        posNdc.setZ(0.0)
        if button == QtCore.Qt.MiddleButton:
            self.camera.processMousePan(posNdc, event.type())

        self.update()

        return super().mouseReleaseEvent(event)

    def wheelEvent(self, event: QtGui.QWheelEvent):
        """
        Re-implement QGraphicsView's wheelEvent handler for smooooooth zoom.
        [https://wiki.qt.io/Smooth_Zoom_In_QGraphicsView]
        Args:
            event (QtGui.QWheelEvent): the event

        Returns:

        """

        numDegrees = event.angleDelta().y() / 8
        numSteps = numDegrees / 15
        self.numScheduledScalings += numSteps
        if self.numScheduledScalings * numSteps < 0:
            # Reset previously scheduled scalings if user changed wheel direction.
            self.numScheduledScalings = numSteps

        anim = QtCore.QTimeLine(350, self)
        anim.setUpdateInterval(8)

        anim.valueChanged.connect(partial(self.scalingTime))
        anim.finished.connect(partial(self.animFinished))
        anim.start()

    @QtCore.pyqtSlot()
    def scalingTime(self):
        factor = 1.0 + self.numScheduledScalings / 300.
        self.camera.processWheel(factor, float(self.width()), float(self.height()), self.m_deltatime)
        self.update()

    @QtCore.pyqtSlot()
    def animFinished(self):
        if self.numScheduledScalings > 0:
            self.numScheduledScalings -= 1
        else:
            self.numScheduledScalings += 1

    def keyPressEvent(self, event: QtGui.QKeyEvent):
        key = event.key()
        self.camera.processKbd(key, self.m_deltatime)
        self.update()

    def flush(self):
        self.axGrid.destroy()


if __name__ == '__main__':
    import sys

    QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_UseDesktopOpenGL)
    QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_ShareOpenGLContexts)
    fmt = QtGui.QSurfaceFormat.defaultFormat()
    fmt.setVersion(2, 0)  # Request 2.0, that's the minimum we can get if PyQt5 was installed with desktop OpenGL
    fmt.setSamples(4)
    fmt.setSwapInterval(1)
    QtGui.QSurfaceFormat.setDefaultFormat(fmt)

    app = QtWidgets.QApplication(sys.argv)

    window = Window(app)
    window.resize(1000, 1000)
    window.show()

    sys.exit(app.exec_())

#graphshader.glsl
#shader vertex
#version 130

in vec3 position;
uniform mat4 u_mvp;
in vec4 color;
out vec4 vertexColor;

void main() {
    gl_Position = vec4(position.xyz, 1.0f);
    vertexColor = color;
}

#shader fragment
#version 130
in vec4 vertexColor;

void main(){
    gl_FragColor = vertexColor;
}

1 Ответ

1 голос
/ 07 апреля 2020

Хорошо, похоже, это решено. Очевидно, использование setAttributeBuffer правильно, в этом нет ничего плохого! Ошибка была в шейдере. Я не умножил матрицу проекции вида-модели!

gl_Position = u_mvp * vec4(position.xyz, 1.0f); исправил это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...