vulkan/图元重启(Primitive restart)

发布时间 2023-12-27 17:52:49作者: 经纬视界

在Vulkan /OpenGL 绘制图形时,可能需要绘制多个并不相连的图形。这样的情况下这几个图形没法被当做一个图形来处理。也就需要多次调用 DrawArrays 或 DrawElements. 如果图形很多,可能会需要用一个循环来调用:

for (int i = 0; i < num_objects; i++) {
    glDrawArrays(GL_TRIANGLES,
                object[n]->first_vertex,
                object[n]->vertex_count);
}

Vulkan 中需要如下扩展: VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME 

Vulkan 中需要启动动态状态标志:VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE_EXT

Vulkan 中使用vkCmdSetPrimitiveRestartEnableEXT(draw_cmd_buffer,VK_TRUE/ VK_FALSE); 启用或者停止该特性 

每一次调用OpenGL 的绘制函数,都需要一定的资源开销,如果每一帧调用太多次,会对程序的性能产生较大的影响。提高性能的办法就是调用一次绘制函数,画出分散的图形。

一种方法是使用 glMultiDrawElements 函数来代替旧的绘制函数,可以减少调用次数,仅需调用此函数一次即可。但是这个函数在OpenGL ES 没有得到支持。而且使用这个函数,

仍然需要将每一个分散的图形维护一组单独的顶点坐标/纹理坐标,这个是免不了的,这些数据仍然需要分开上传,还是会消耗一定的资源。针对这种情况使用图元重启会更加合适。

考虑通常的情况,当用户绘制 GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_LINE_STRIP, GL_LINE_LOOP 这些图元时,所有的绘制点按照特定的顺序被连起来,以形成一个最终的复杂图形,

也就是说最终的复杂图形由多个相连的三角形或线段组成。像上面提到的情况,想要绘制分散的图形,应该怎么办?

图元重启(Primitive restart) 允许用户绘制不连续的、分散的图形。考虑使用 glDrawElements 函数,绘制时按照indices所指定的顶点的顺序来绘制的。此时可以指定某一个值,

该值表示一个重启的标志。遇到这个值的时候,OpenGL不会绘制图元,而是结束上一段绘制,然后重新启动新的绘制,也就是說用后面的索引所指定的顶点来从头绘制一个图形。

举个例子:比如指定8爲重启的标志,遇到8就重启。上面的是不启用图元重启的情况,即通常的情况。下面的是启用图元重启的情况,我们可以看到,从9开始,又重新从头开始绘制Triangle strip了。

指定重启位置的数值,在桌面版的OpenGL是可以自行设定的,glPrimitiveRestartIndex​ 函数指定重启的标志。在OpenGL ES 无法自行指定,只能用给定的值。

首先设置启用图元重启.

glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);

std::array<uint32_t, index_count> indices{0, 4, 3, 7,
UINT32_MAX,
1, 0, 2, 3,
UINT32_MAX,
2, 6, 1, 5,
UINT32_MAX,
1, 5, 0, 4,
UINT32_MAX,
4, 5, 7, 6,
UINT32_MAX,
2, 3, 6, 7};

// Draw triangle strips
glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);

glDrawElements(GL_TRIANGLE_STRIP, 29, GL_UNSIGNED_INT, 0);

另外,如果 glDrawElements 的mode是points等无效。