Изменение данных OpenGL Vertex Buffer Object через pyopengl OpenGL.arrays.vbo не имеет никакого эффекта

Я застрял, пытаясь изменить данные в VBO.

Я настроил сцену с двумя примитивами Triangle, используя VBO через вспомогательный класс python OpenGL.arrays.vbo. Это сработало.

Затем я хочу изменить данные (в приведенном ниже минимальном примере просто сдвинуть одну вершину при нажатии кнопки), которые я не могу заставить работать. Я не уверен, правильно ли я использую VBO или есть какая-то тривиальность, блокирующая перерисовку на стороне PyQt5.

Ниже приведен полный минимальный пример, важные вещи выполняются в функциях-членах initializeGL, paintGL и shift.

Внутри GLWidget.shift я пробовал разные подходы, следуя документам и этому ответу безуспешно. Любая помощь приветствуется.

#!/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

person flonk    schedule 09.04.2020    source источник


Ответы (1)


Списки Siplay (glGenList) устарели. Вы пытаетесь закодировать в списке спецификацию вершин.
Я рекомендую вместо этого использовать объект Vertex Array.

Создайте 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)

Обратите внимание, список отображения не работает, так как некоторые команды не компилируются в список отображения, а выполняются немедленно, в том числе glVertexPointer и glColorPointer. См. glNewList.

person Rabbid76    schedule 09.04.2020
comment
Спасибо, ваш пример работает. Однако, зачем Вам по-прежнему хранить VBO-экземпляр, ведь он больше не нужен, верно? Первоначально моей целью было использовать VBO, а не VAO, но ваш совет решил мою проблему, поэтому я принимаю это как ответ. - person flonk; 09.04.2020
comment
Просто чтобы понять как можно больше из вашего ответа: есть ли причина, по которой вы предлагаете VAO вместо VBO? Этот выбор не зависит от важного указания не использовать списки отображения, верно? Я бы предположил, что для гораздо большего набора данных, в котором обновляется только несколько вершин, VBO должны работать лучше, это правильно? - person flonk; 09.04.2020
comment
@flonk Вам нужны оба, vbo и vao. На vbo ссылается vao. Когда вызывается glVertexPointer (и т. д.), текущий связанный vbo связан (для указанного атрибута) в векторе состояния текущего связанного vao. vao хранит все спецификации, которые необходимы для рисования меша, но не хранит данные вершин (он просто ссылается на данные вершин с помощью vbo). Таким образом, достаточно обновить данные в vbo, не обновляя vao. - person Rabbid76; 09.04.2020
comment
Я думаю, что при использовании класса OpenGL.arrays.vbo-Helper мне не нужно явно иметь дело с VAO, но я могу продолжить работу с командами, как в моем коде, после отключения списков отображения. Если Вы не согласны и заинтересованы, я могу опубликовать код (?) - person flonk; 09.04.2020