PyOpenGL - 当一些场景元素需要消失时更新三角形数组

PyOpenGL - update triangles array when some of the scene elements need to disappear

我有一个场景需要快速渲染。所以我成功地将三角形的顶点生成为 numpy,上传到 GPU 并非常快速地渲染它。到目前为止一切顺利。

当场景中的某些元素需要从场景中消失时,问题就出现了。我做的是遍历整个数组,过滤掉不再需要绘制的场景元素的顶点,重新上传到GPU再渲染。这显然需要几秒钟,这是不可接受的。我正在寻找一种快速修改数组和重绘场景的方法。

这是我使用的代码的简化版本:(也希望对代码本身有任何评论)

from OpenGL.GL import *
from OpenGL.GLU import *
import numpy as np
import ctypes

vertex_dtype = [("position", np.float32, 3), ("color", np.float32, 4)]
vertex_array_object = None
vertex_buffer = None
shader_program = .......

def upload_to_gpu(vertices_np):
    global vertex_array_object, vertex_buffer
    if vertex_array_object is None:
        vertex_array_object = glGenVertexArrays(1)
    if vertex_buffer is None:
        vertex_buffer = glGenBuffers(1)
    glBindVertexArray(vertex_array_object)
    glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer)
    glBufferData(GL_ARRAY_BUFFER, vertices_np.nbytes, vertices_np, GL_STATIC_DRAW)
    stride = vertices_np.strides[0]
    offset = ctypes.c_void_p(0)
    position_location = glGetAttribLocation(shader_program, 'a_position')
    glEnableVertexAttribArray(position_location)
    glVertexAttribPointer(position_location, 3, GL_FLOAT, False, stride, offset)

    offset = ctypes.c_void_p(vertices_np.dtype["position"].itemsize)
    color_location = glGetAttribLocation(shader_program, "a_color")
    glEnableVertexAttribArray(color_location)
    glVertexAttribPointer(color_location, 4, GL_FLOAT, False, stride, offset)

    glBindVertexArray(0)
    glBindBuffer(GL_ARRAY_BUFFER, 0)

def create_np_array(all_vertices):
    vertices_np = np.zeros(len(all_vertices), dtype=vertex_dtype)
    cur_idx = 0
    for vertex in all_vertices:        
        if ....should_ignore_triangle....:
            continue
        vertices_np[cur_idx]["position"] = [vertex[0], vertex[1], vertex[2]]
        vertices_np[cur_idx]["color"] = ......
        cur_idx += 1

    vertices_np = vertices_np[:cur_idx]
    return vertices_np

def draw_scene(vertices_np):
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    glUseProgram(shader_program)
    # Set view and projection matrix    
    glBindVertexArray(vertex_array_object)
    glDrawArrays(GL_TRIANGLES, 0, vertices_np.shape[0])

def main():
    all_vertices = [[0, 0, 0], [1, 0, 0], [0, 1, 0], ......]   # long list of ~million vertices
    vertices_np = create_np_array(all_vertices)
    upload_to_gpu(vertices_np)

    # Now we are ready to start rendering
    draw_scene(vertices_np) # works fine

    # Now something changes and we need to regenerate the vertices_np and upload it again
    vertices_np = create_np_array(all_vertices)   # <-- THIS TAKES TOO MUCH TIME
    upload_to_gpu(vertices_np)
    draw_scene(vertices_np) # works fine

如果你只想省略顶点,但不想添加新的顶点,那么我建议只绘制你想要的顶点子集。不要更改 GPU 上的任何数据或重新创建任何顶点数组。

创建一个元组数组,其中包含要绘制的顶点的索引范围。为此,找到必须省略的第一个顶点并将 "range" 元组附加到元组列表。找到下一个应该绘制的顶点并重复该过程直到数组的末尾:

def draw_np_array(all_vertices):

    idx_list = []
    cur_start_idx = 0
    cur_end_idx = 0
    for vertex in all_vertices:

        if ....should_ignore_triangle....:
            if cur_end_idx > cur_start_idx:
                idx_list.append( (cur_start_idx, cur_end_idx) );
            cur_end_idx += 1
            cur_start_idx = cur_end_idx
            continue

        cur_end_idx += 1

    if cur_end_idx > cur_start_idx:
        idx_list.append( (cur_start_idx, cur_end_idx) );

    return idx_list

使用 "intelligent" 绘图调用绘制场景:

def draw_scene(idx_list):
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    glUseProgram(shader_program)
    # Set view and projection matrix
    glBindVertexArray(vertex_array_object)

    for idx_range in idx_list:
        start = idx_range[0]
        count = idx_range[1]-idx_range[0]
        glDrawArrays(GL_TRIANGLES, start, count)

您可以使用 glMultiDrawArrays 进一步改进这一点,它可以通过一次绘制调用绘制数组的多个范围,而不是在循环中绘制数组。

老实说,我不太明白你的代码。 "filter out the vertices" 是什么意思?就像删除包含某个顶点的所有三角形一样?您是事先有索引缓冲区还是程序生成的?

无论如何,您可以为每个顶点设置一个带有标志(true/false0 / 1)的附加布局。在片段着色器中,只需检查此变量(是否已插值)是否大于 0。如果它更大 - 你绘制像素。如果不是 - 你 discard 它。这可能是最简单的方法,可以在几分钟内实施。