我想在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()
答案 0 :(得分:2)
要完全回答这个问题,我首先要提到OpenGL缓冲区更新。
OpenGL指令glBufferData
创建并初始化缓冲区对象的数据存储。缓冲区对象的现有数据存储区被完全破坏,并创建了一个新的数据存储区(可能具有不同的大小)。如果将数据指针传递给该函数,则数据存储将完全由数据初始化。假定缓冲区的大小和提供的数据的大小相等。
glBufferSubData
更新现有数据存储区的全部数据或部分数据。假设数据存储区是由glBufferData
之前创建的。没有数据存储被破坏或创建。
当然,从技术上讲,glBufferData
始终可以代替glBufferSubData
使用,但是glBufferSubData
的性能要好得多,因为省去了昂贵的缓冲区创建(分配)工作。
使用
self.vbo.set_array(self.verts)
是个坏主意,因为在实现(PyOpenGL/OpenGL/arrays/vbo.py中可以看到,此方法创建了一个全新的缓冲区,其大小可能是新的,并且将强制重新创建缓冲区对象的数据存储区(因为{ {1}}。
如果以前创建了缓冲区,那么self.copied = False
将以self.vbo.copy_data()
更新数据(请参见glBufferSubData
中的if self.copied:
)。为了使这项工作有效,缓冲区必须是当前绑定的缓冲区(copy_data
)。此外,必须设置一个副本信息(self.vbo.bind()
)。复制信息由项目设置器(VBO.copy_segments
)声明。
这意味着,在“方法1”中,您将必须执行以下操作:
VBO.__setitem__
由于OpenGL.arrays.vbo
只不过是OpenGL缓冲区指令的包装器,所以我更愿意直接使用self.vbo[:] = self.verts
self.vbo.bind()
self.vbo.copy_data()
,这样在以下情况下效果最佳:
glBufferSubData
使用这种方法,甚至可以更新数据存储的子集。注意# Approach 3:
# Direct buffer update by `glBufferSubData`
self.vbo.bind()
self.vbo.implementation.glBufferSubData(self.vbo.target, 0, self.vbo.data)
的第二个参数是缓冲区对象数据存储区的字节偏移量。此外,还有一个重载的实现,它可以处理缓冲区大小和直接数据指针。