Search code examples
pythonopenglvbopyglet

How to update data with a VBO and Pyglet


I would like to make a mesh with Pyglet that is changing every frame. Therefore I need to update the vertices very often and I thought that a VBO would be the fastest way to go here (correct me if I am wrong). Below an example for Points. Is this the correct way of doing it? I read that the number of glBindBuffer calls should be minimised, but here it is called every frame. also GL_DYNAMIC_DRAW is enabled, but if I change it to GL_STATIC_DRAW it is still working. It makes me wondering if this is a correct setup for fast computation

import pyglet
import numpy as np
from pyglet.gl import *
from ctypes import pointer, sizeof

vbo_id = GLuint()
glGenBuffers(1, pointer(vbo_id))

window = pyglet.window.Window(width=800, height=800)

glClearColor(0.2, 0.4, 0.5, 1.0)

glEnableClientState(GL_VERTEX_ARRAY)

c = 0

def update(dt):
    global c
    c+=1
    data = (GLfloat*4)(*[500+c, 100+c,300+c,200+c])
    glBindBuffer(GL_ARRAY_BUFFER, vbo_id)
    glBufferData(GL_ARRAY_BUFFER, sizeof(data), 0, GL_DYNAMIC_DRAW)
    glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(data), data)


pyglet.clock.schedule(update)

glPointSize(10)

@window.event
def on_draw():

    glClear(GL_COLOR_BUFFER_BIT)
    glColor3f(0, 0, 0)

    glVertexPointer(2, GL_FLOAT, 0, 0)
    glDrawArrays(GL_POINTS, 0, 2)


pyglet.app.run()

Solution

  • You don't need to call glBufferData every single time in update - create and fill the VBO once (see setup_initial_points) and only update it with glBufferSubData. In case you are only working with a single VBO, you can also comment out the glBindBuffer call in update() (see code below). GL_DYNAMIC_DRAW vs GL_STATIC_DRAW won't make a big difference in this example since you are pushing very few data onto the GPU.

    import pyglet
    from pyglet.gl import *
    from ctypes import pointer, sizeof
    
    window = pyglet.window.Window(width=800, height=800)
    
    ''' update function  '''
    c = 0
    def update(dt):
        global c
        c+=1
        data = calc_point(c)
        # if there's only on VBO, you can comment out the 'glBindBuffer' call
        glBindBuffer(GL_ARRAY_BUFFER, vbo_id)
        glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(data), data)
    
    pyglet.clock.schedule(update)
    
    
    ''' draw function  '''
    @window.event
    def on_draw():
    
        glClear(GL_COLOR_BUFFER_BIT)
        glColor3f(0, 0, 0)
    
        glVertexPointer(2, GL_FLOAT, 0, 0)
        glDrawArrays(GL_POINTS, 0, 2)
    
    
    ''' calculate coordinates given counter 'c' '''
    def calc_point(c):
        data = (GLfloat*4)(*[500+c, 100+c, 300+c, 200+c])
        return data
    
    
    ''' setup points '''
    def setup_initial_points(c):
        vbo_id = GLuint()
        glGenBuffers(1, pointer(vbo_id))
    
        data = calc_point(c)
        glBindBuffer(GL_ARRAY_BUFFER, vbo_id)
        glBufferData(GL_ARRAY_BUFFER, sizeof(data), 0, GL_DYNAMIC_DRAW)
    
        return vbo_id
    
    
    ############################################
    
    vbo_id = setup_initial_points(c)
    
    glClearColor(0.2, 0.4, 0.5, 1.0)
    glEnableClientState(GL_VERTEX_ARRAY)
    
    glPointSize(10)
    pyglet.app.run()