PyOpenGL - обновлять массив треугольников, когда некоторые элементы сцены должны исчезнуть - PullRequest
0 голосов
/ 01 ноября 2018

У меня есть сцена, которую нужно быстро отрендерить. Таким образом, я успешно генерирую вершины треугольников как numpy, загружаю их в 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

Ответы [ 2 ]

0 голосов
/ 01 ноября 2018

Если честно, я не совсем понимаю ваш код. Что вы подразумеваете под «отфильтровывать вершины»? Как удалить все треугольники, которые включают в себя определенную вершину? А у вас есть индексный буфер заранее или вы генерируете его процедурно?

В любом случае, вы можете просто установить дополнительный макет с флагом (true / false или 0 / 1) для каждой вершины. А в фрагментном шейдере просто проверьте, больше ли эта переменная (интерполированная или нет) больше 0 или нет. Если оно больше - нарисуй пиксель. Если это не так - вы discard это. Это, вероятно, самый простой способ, и его можно реализовать за несколько минут.

0 голосов
/ 01 ноября 2018

Если вы хотите пропустить только вершины, но не хотите добавлять новые, я рекомендую просто нарисовать нужное подмножество вершин. Не изменяйте данные на графическом процессоре и не создавайте заново массив вершин.

Создает массив кортежей, который содержит диапазоны индексов вершин, которые будут нарисованы. Для этого найдите первую вершину, которую нужно опустить, и добавьте кортеж «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

Нарисуйте сцену с помощью "интеллектуального" вызова для рисования:

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, который может рисовать несколько диапазонов массива одним вызовом отрисовки вместо рисования массивов в цикле.

...