最好更新一个小的顶点缓冲区,还是发送一个统一的?

时间:2014-08-18 13:29:56

标签: c++ opengl user-interface shader

我正在为我的OpenGL(核心配置文件)游戏引擎编写/规划GUI渲染器,而且我不完全确定如何为我的四边形表示顶点数据。到目前为止,我已经想到了两种可能的解决方案:

1)直截了当的方式,每GuiElement跟踪它自己的顶点数组对象,包含2d屏幕坐标和纹理坐标,并更新(glBufferSubData())任何时候GuiElement移动或调整大小。

2)我全局存储一个顶点数组对象,其坐标为(0,0)(1,0)(0,1)(1,1),并将rect上传为vec4制服(x,y,w,h)每一帧,并转换顶点着色器(vertex.xy *= guiRect.zw; vertex.xy += guiRect.xy;)中的顶点位置。

我知道方法#2 可以,但我想知道哪个更好

3 个答案:

答案 0 :(得分:1)

对于GUI元素,您可以使用动态顶点缓冲区(环形缓冲区)并且每帧都上传几何体,因为这是非常少量的几何数据。然后,您可以批量处理GUI元素渲染,这与您提出的两种方法不同。

如果渲染大量GUI元素(例如文本),则批处理非常重要。你可以很容易地构建一个通用的GUI渲染系统,它可以缓存GUI元素绘制调用,并在状态发生变化时将绘制刷新到GPU。

答案 1 :(得分:1)

我确实喜欢选项二的想法,但是,它会非常低效,因为它需要对每个元素进行绘制调用。正如其他回复中所提到的,最大的性能提升在于批处理几何和减少绘制调用的数量。 (换句话说,减少应用程序花费与GL驱动程序通信的时间)。

所以我认为用OpenGL绘制2D对象的最快方法是使用类似于你的选项的技术,但是添加批处理。

为了在屏幕上绘制四边形,您需要的最小顶点格式是简单的vec2,每个四边形有4 vec2 s。纹理坐标可以在非常轻量级的顶点着色器中生成,例如:

// xy = vertex position in normalized device coordinates ([-1,+1] range).
attribute vec2 vertexPositionNDC;

varying vec2 vTexCoords;

const vec2 scale = vec2(0.5, 0.5);

void main()
{
    vTexCoords  = vertexPositionNDC * scale + scale; // scale vertex attribute to [0,1] range
    gl_Position = vec4(vertexPositionNDC, 0.0, 1.0);
}

在应用程序端,您可以通过使用两个顶点缓冲区来设置双缓冲区以优化吞吐量,这样您就可以在给定帧上写入其中一个缓冲区然后翻转缓冲区并将其发送到GL,同时启动立即写入下一个缓冲区:

// Update:
GLuint vbo = vbos[currentVBO];
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferSubData(GL_ARRAY_BUFFER, dataOffset, dataSize, data);

// Draw:
glDrawElements(...);

// Flip the buffers:
currentVBO = (currentVBO + 1) % NUM_BUFFERS;

或者另一个更简单的选择是使用单个缓冲区,但在每次提交时分配新存储,以避免阻塞,如下所示:

glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, dataSize, data, GL_STREAM_DRAW);

这是一种众所周知的用于简单异步数据传输的技术。 Read this了解更多信息。

使用索引几何也是个好主意。使用顶点缓冲区保留无符号短路的索引缓冲区。每个元素IB的2字节将减少数据流量,并且应该具有足够大的索引范围,以适应您可能希望绘制的任何数量的2D / UI元素。

答案 2 :(得分:0)

我建议像DXUT那样做,它从每个元素获取rect,并使用一个通用方法呈现它们,该方法将元素作为参数,其中包含一个rect。每个控件都可以有许多元素。它以STREAM_DRAW模式和常量索引缓冲区的特定顺序将rect的四个点添加到缓冲区。这确实是单独绘制每个矩形,但性能并不是完全重要的,因为几何图形很简单,当您在对话框中时,通常可以将3d场景的渲染放在后面。编辑:即使使用它来做HUD项目,它的性能损失可以忽略不计。

这是一种简单而有条理的方法,它可以很好地处理纹理,并且只有两个着色器,一个用于绘制纹理组件,另一个用于非纹理组件。有一种特殊的方式来做文本。

如果你想看看我是怎么做的,你可以看一下: https://github.com/kevinmackenzie/ObjGLUF

在GLUFGui.h / .cpp