pyopengl - 动态更新顶点缓冲区对象中的值 [英] pyopengl - dynamically updating values in a vertex buffer object

查看:100
本文介绍了pyopengl - 动态更新顶点缓冲区对象中的值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在 PyOpenGL 中创建具有可拖动顶点的多边形.仔细阅读后,VBO 似乎是实现这一目标的明智方法.

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

这是一个愚蠢的例子:

from OpenGL import GL as gl从 OpenGL 导入 GLUT 作为 glut从 OpenGL.arrays 导入 vbo将 numpy 导入为 npVBOJiggle 类(对象):def __init__(self,nvert=100,jiggliness=0.01):self.nvert = nvertself.jiggliness = 抖动verts = 2*np.random.rand(nvert,2) - 1self.verts = np.require(verts,np.float32,'F')self.vbo = vbo.VBO(self.verts)定义绘制(自我):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抖动(自我):# 随机摇晃一半的顶点delta = (np.random.rand(self.nvert//2,2) - 0.5)*self.jigglinessself.verts[:self.nvert:2] += delta# vbo的data属性和numpy数组一样# 顶点断言 self.verts 是 self.vbo.data# # 方法一:# # 看起来这应该有效,但它没有 - 所有的# # 顶点保持静态,即使 vbo 的数据被更新# self.vbo.copy_data()# 方法二:# 这有效,但似乎没有必要复制整个数组# 直到 GPU,特别是如果数组很大并且我有# 只修改了一小部分顶点self.vbo.set_array(self.verts)如果 __name__ == '__main__':glut.glutInit()glut.glutInitDisplayMode(glut.GLUT_DOUBLE | glut.GLUT_RGB)glut.glutInitWindowSize(250, 250)glut.glutInitWindowPosition(100, 100)glut.glutCreateWindow( 无 )演示 = VBOJiggle()glut.glutDisplayFunc(demo.draw)glut.glutIdleFunc(demo.draw)glut.glutMainLoop()

解决方案

要完整回答这个问题,不得不先提一下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.vertsself.vbo.bind()self.vbo.copy_data()

由于 OpenGL.arrays.vbo 只不过是 OpenGL 缓冲区指令的包装器,我更喜欢直接使用 glBufferSubData,这样在这种情况下效果最好:

# 方法三:# 通过 `glBufferSubData` 直接更新缓冲区self.vbo.bind()self.vbo.implementation.glBufferSubData(self.vbo.target, 0, self.vbo.data)

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

I'd like to create polygons with draggable vertices in PyOpenGL. Having read around a bit, VBOs seemed like a sensible way to achieve this.

Having never used VBOs before, I'm having trouble figuring out how to dynamically update them - ideally I'd like to just modify elements of a numpy array of vertices, then propagate only the elements that changed up to the GPU. I had assumed that the OpenGL.arrays.vbo.VBO wrapper did this automagically with its copy_data() method, but it seems not.

Here's a silly example:

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()

解决方案

To completely answer this question, I have to mention the OpenGL buffer update first.

The OpenGL instruction glBufferData creates and initializes a buffer object's data store. An existing data store of an buffer object is completely destroyed and a new data store (possibly with a different size) is created. If a data pointer is passed to the function, then the data store is completely initialized by the data. The size of the buffer and the size of the provided data is assumed to be equal.

glBufferSubData updates the entire data or a subset of the data of an existing data store. The data store is assumed to be created before, by glBufferData. No data store is destroyed or created.

Of course, technically glBufferData can always be use instead of glBufferSubData, but glBufferSubData will perform much better, because the expensive buffer creation (allocation) is eliminated.

Using

self.vbo.set_array(self.verts)

is a bad idea, because as seen in the implementation (PyOpenGL/OpenGL/arrays/vbo.py), this method creates a completely new buffer, with a possibly new size and will force the recreation of the buffer object's data store (because of self.copied = False).

If the buffer was created before, then self.vbo.copy_data() will update the data by glBufferSubData (see if self.copied: in copy_data). To make this work the buffer has to be the currently bound buffer (self.vbo.bind()). Further a copy information has to be set (VBO.copy_segments). The copy information is stated by the item setter (VBO.__setitem__).

This means, in "Approach 1" you would have to do something like the following:

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

Since OpenGL.arrays.vbo is nothing more than a wrapper for the OpenGL buffer instructions, I would prefer to use glBufferSubData directly, which will perform best in cases like this:

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

With this approach even subsets of the data store can be updated. Note the 2nd parameter of glBufferSubData is a byte offset to the buffer objects data store. Further there is an overloaded implementation, which can process a buffer size and a direct data pointer.

这篇关于pyopengl - 动态更新顶点缓冲区对象中的值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆