【问题标题】:Changing OpenGL Vertex Buffer Object data via pyopengl OpenGL.arrays.vbo has no effect通过 pyopengl OpenGL.arrays.vbo 更改 OpenGL 顶点缓冲区对象数据无效
【发布时间】:2020-07-21 20:26:49
【问题描述】:

我一直在尝试更改 VBO 中的数据。

我通过 python OpenGL.arrays.vbo 辅助类使用 VBO 设置了具有 2 个三角形基元的场景。那行得通。

然后我想更改我无法使用的数据(在下面的最小示例中,只需在单击按钮时移动一个顶点)。我不确定我是否错误地使用了 VBO,或者是否有一些琐碎的事情阻止了 PyQt5 端的重绘。

下面是完整的最小示例,重要的东西在成员函数initializeGLpaintGLshift中发挥作用。

GLWidget.shift 内部,我按照文档和this answer 尝试了不同的方法,但没有成功。任何帮助表示赞赏。

#!/usr/bin/env python

import ctypes
import sys
import numpy as np
import OpenGL.arrays.vbo as glvbo

from PyQt5.QtCore import QSize
from PyQt5.QtWidgets import (QApplication, QHBoxLayout, QOpenGLWidget,
                             QWidget, QPushButton)

import OpenGL.GL as gl

class Window(QWidget):

    def __init__(self):
        super(Window, self).__init__()

        self.glWidget = GLWidget()
        button = QPushButton('shift', self)
        button.clicked.connect(self.glWidget.shift)

        layout = QHBoxLayout()
        layout.addWidget(self.glWidget)
        layout.addWidget(button)

        self.setLayout(layout)

class GLWidget(QOpenGLWidget):

    def __init__(self, parent=None):
        super().__init__(parent)
        self.object = None

    def minimumSizeHint(self):
        return QSize(400, 400)

    def initializeGL(self):
        gl.glClearColor(0., 0., 0., 0.)

        # a red and a green triangle
        self.vertices = np.array([
            # <- x,y,z ----->  <- r,g,b -->
            -0.5, -0.2, 0.0, 1.0, 0.0, 0.0,
             0.5, -0.5, 0.0, 1.0, 0.0, 0.0,
             0.5, 0.5,  0.0, 1.0, 0.0, 0.0,
             0.4, -0.2, 0.0, 0.0, 1.0, 0.0,
             1.4, -0.5, 0.0, 0.0, 1.0, 0.0,
             1.4, 0.5,  0.0, 0.0, 1.0, 0.0,
        ], 'f')

        self.vbo = glvbo.VBO(self.vertices)
        self.vbo.bind()

        self.object = gl.glGenLists(1)

        gl.glNewList(self.object, gl.GL_COMPILE)

        gl.glEnableClientState(gl.GL_VERTEX_ARRAY)
        gl.glEnableClientState(gl.GL_COLOR_ARRAY)

        buffer_offset = ctypes.c_void_p
        stride = (3+3)*self.vertices.itemsize

        gl.glVertexPointer(3, gl.GL_FLOAT, stride, None)
        gl.glColorPointer(3, gl.GL_FLOAT, stride, buffer_offset(12))

        gl.glDrawArrays(gl.GL_TRIANGLES, 0, 6)
        gl.glDisableClientState(gl.GL_VERTEX_ARRAY)
        gl.glDisableClientState(gl.GL_COLOR_ARRAY)

        gl.glEndList()
        gl.glShadeModel(gl.GL_FLAT)

    def paintGL(self):
        gl.glClear(
            gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
        gl.glLoadIdentity()
        gl.glRotated(50.0, 0.0, 1.0, 0.0)
        gl.glCallList(self.object)

    def resizeGL(self, width, height):
        side = min(width, height)
        if side < 0:
            return

        gl.glViewport((width - side) // 2, (height - side) // 2, side,
                           side)

        gl.glMatrixMode(gl.GL_PROJECTION)
        gl.glLoadIdentity()
        gl.glOrtho(-1., +1., -1., +1., -100.0, 100.0)
        gl.glMatrixMode(gl.GL_MODELVIEW)

    def shift(self):
        # shift y-position of one vertex
        self.vertices[1] += 10.3
        assert self.vertices is self.vbo.data

        # version 1
        # self.vbo.implementation.glBufferSubData(self.vbo.target, 0, self.vbo.data)

        # version 2
        # self.vbo[:] = self.vertices[:]
        # self.vbo.bind()
        # self.vbo.copy_data()

        # version 2b (use slice)
        # self.vbo[1:2] = self.vertices[1:2]
        # self.vbo.bind()
        # self.vbo.copy_data()

        # version 3
        self.vbo.set_array(self.vertices)
        self.vbo.bind()
        self.vbo.copy_data()

        self.update()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())

代码在Ubuntu 18.04 机器上运行在python 3.6

  Vendor: Intel Open Source Technology Center
  Renderer: Mesa DRI Intel(R) HD Graphics 5500 (Broadwell GT2)
  OpenGL Version: 3.0 Mesa 19.2.8
  Shader Version: 1.30

【问题讨论】:

    标签: python opengl pyqt pyqt5 pyopengl


    【解决方案1】:

    不推荐使用 Sisplay 列表 (glGenList)。您尝试在列表中编码的是Vertex Specification
    我建议改用Vertex Array Object

    在指定通用顶点属性数据数组之前创建 VAO:

    class GLWidget(QOpenGLWidget):
        # [...]
    
        def initializeGL(self):
            # [...]
    
            self.vbo = glvbo.VBO(self.vertices)
            self.vbo.bind()
    
            self.vao = gl.glGenVertexArrays(1)
            gl.glBindVertexArray(self.vao)
    
            gl.glEnableClientState(gl.GL_VERTEX_ARRAY)
            gl.glEnableClientState(gl.GL_COLOR_ARRAY)
    
            buffer_offset = ctypes.c_void_p
            stride = (3+3)*self.vertices.itemsize
            gl.glVertexPointer(3, gl.GL_FLOAT, stride, None)
            gl.glColorPointer(3, gl.GL_FLOAT, stride, buffer_offset(12))
    
            gl.glBindVertexArray(0)
    

    当你要绘制对象时,绑定VAO就足够了:

    class GLWidget(QOpenGLWidget):
        # [...]
    
        def paintGL(self):
            gl.glClear(
                gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
    
            gl.glLoadIdentity()
            gl.glRotated(50.0, 0.0, 1.0, 0.0)
    
            gl.glBindVertexArray(self.vao)
            gl.glDrawArrays(gl.GL_TRIANGLES, 0, 6)
            gl.glBindVertexArray(0)
    

    注意,显示列表不起作用,因为某些命令没有编译到显示列表中而是立即执行,包括glVertexPointerglColorPointer。见glNewList

    【讨论】:

    • 谢谢,您的示例有效。但是,你为什么还要保留VBO-instance,它应该不再需要了,对吧?最初我的目标是使用 VBO,而不是 VAO,但你的提示解决了我的问题,所以我接受这个作为答案。
    • 只是为了从您的回答中尽可能多地理解:您是否有理由建议使用 VAO 而不是 VBO?这个选择与不使用显示列表的重要提示无关,对吧?我会假设对于一个更大的数据集,其中只有几个顶点被更新,VBO 应该表现得更好,对吗?
    • @flonk 你需要 vbo 和 vao。 vbo 被 vao 引用。当glVertexPointer(等)被调用时,当前绑定的vbo在当前绑定的vao的状态向量中关联(针对指定的属性)。 vao 存储了绘制网格所需的所有规范,但它不存储顶点数据(它只是通过 vbo 引用顶点数据)。因此,只需更新 vbo 中的数据,而无需更新 vao。
    • 我认为使用OpenGL.arrays.vbo-Helper 类我不需要明确处理VAO,但可以在从显示列表切换后继续执行我的代码中的命令。如果您不同意并且有兴趣,我可以发布代码(?)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-30
    • 1970-01-01
    相关资源
    最近更新 更多