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
/false
或 0
/ 1
)的附加布局。在片段着色器中,只需检查此变量(是否已插值)是否大于 0。如果它更大 - 你绘制像素。如果不是 - 你 discard
它。这可能是最简单的方法,可以在几分钟内实施。
我有一个场景需要快速渲染。所以我成功地将三角形的顶点生成为 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
/false
或 0
/ 1
)的附加布局。在片段着色器中,只需检查此变量(是否已插值)是否大于 0。如果它更大 - 你绘制像素。如果不是 - 你 discard
它。这可能是最简单的方法,可以在几分钟内实施。