3

我想在PyOpenGL. 阅读了一下,VBO 似乎是实现这一目标的明智方法。

以前从未使用过 VBO,我无法弄清楚如何动态更新它们 - 理想情况下,我只想修改numpy顶点数组的元素,然后仅将更改的元素传播到 GPU。我曾假设OpenGL.arrays.vbo.VBO包装器使用它的copy_data()方法自动执行此操作,但似乎不是。

这是一个愚蠢的例子:

from OpenGL import GL as gl
from OpenGL import GLUT as glut
from OpenGL.arrays import vbo
import numpy as np

class VBOJiggle(object):

    def __init__(self,nvert=100,jiggliness=0.01):
        self.nvert = nvert
        self.jiggliness = jiggliness

        verts = 2*np.random.rand(nvert,2) - 1
        self.verts = np.require(verts,np.float32,'F')
        self.vbo = vbo.VBO(self.verts)

    def draw(self):

        gl.glClearColor(0,0,0,0)
        gl.glClear(gl.GL_COLOR_BUFFER_BIT)

        gl.glEnableClientState(gl.GL_VERTEX_ARRAY)

        self.vbo.bind()

        gl.glVertexPointer(2,gl.GL_FLOAT,0,self.vbo)
        gl.glColor(0,1,0,1)
        gl.glDrawArrays(gl.GL_LINE_LOOP,0,self.vbo.data.shape[0])

        gl.glDisableClientState(gl.GL_VERTEX_ARRAY)

        self.vbo.unbind()

        self.jiggle()

        glut.glutSwapBuffers()

    def jiggle(self):

        # jiggle half of the vertices around randomly
        delta = (np.random.rand(self.nvert//2,2) - 0.5)*self.jiggliness
        self.verts[:self.nvert:2] += delta

        # the data attribute of the vbo is the same as the numpy array
        # of vertices
        assert self.verts is self.vbo.data

        # # Approach 1:
        # # it seems like this ought to work, but it doesn't - all the
        # # vertices remain static even though the vbo's data gets updated
        # self.vbo.copy_data()

        # Approach 2:
        # this works, but it seems unnecessary to copy the whole array
        # up to the GPU, particularly if the array is large and I have
        # modified only a small subset of vertices
        self.vbo.set_array(self.verts)

if __name__ == '__main__':
    glut.glutInit()
    glut.glutInitDisplayMode( glut.GLUT_DOUBLE | glut.GLUT_RGB )
    glut.glutInitWindowSize( 250, 250 )
    glut.glutInitWindowPosition( 100, 100 )
    glut.glutCreateWindow( None )

    demo = VBOJiggle()
    glut.glutDisplayFunc( demo.draw )
    glut.glutIdleFunc( demo.draw )

    glut.glutMainLoop()
4

1 回答 1

2

要完全回答这个问题,我不得不先提一下OpenGL缓冲区更新。

OpenGL 指令glBufferData创建并初始化缓冲区对象的数据存储。缓冲区对象的现有数据存储被完全破坏,并创建一个新的数据存储(可能具有不同的大小)。如果将数据指针传递给函数,则数据存储完全由数据初始化。假设缓冲区的大小和提供的数据的大小相等。

glBufferSubData更新现有数据存储的整个数据或数据子集。假设数据存储是之前创建的,由glBufferData. 没有数据存储被破坏或创建。

当然,从技术上讲glBufferData,总是可以使用而不是glBufferSubData,但glBufferSubData性能会更好,因为消除了昂贵的缓冲区创建(分配)。

使用

self.vbo.set_array(self.verts)

是个坏主意,因为从实现(PyOpenGL/OpenGL/arrays/vbo.py)中可以看出,此方法创建一个全新的缓冲区,可能具有新的大小,并将强制重新创建缓冲区对象的数据存储(因为self.copied = False)。

如果缓冲区是之前创建的,那么self.vbo.copy_data()将更新数据glBufferSubData(参见if self.copied:参考资料copy_data)。为了使这项工作,缓冲区必须是当前绑定的缓冲区(self.vbo.bind())。此外,必须设置复制信息 ( VBO.copy_segments)。复制信息由项目设置者 ( VBO.__setitem__) 声明。

这意味着,在“方法 1”中,您必须执行以下操作:

self.vbo[:] = self.verts
self.vbo.bind()
self.vbo.copy_data()

由于OpenGL.arrays.vbo它只不过是 OpenGL 缓冲区指令的包装器,因此我更愿意glBufferSubData直接使用,这样在以下情况下性能最佳:

# Approach 3:
# Direct buffer update by `glBufferSubData`
self.vbo.bind()
self.vbo.implementation.glBufferSubData(self.vbo.target, 0, self.vbo.data)

使用这种方法,甚至可以更新数据存储的子集。请注意,第二个参数glBufferSubData是缓冲区对象数据存储的字节偏移量。此外还有一个重载实现,它可以处理缓冲区大小和直接数据指针。

于 2019-03-17T17:22:46.020 回答