我是openGL的新手,我正在开发一个应用程序,我需要绘制几个带有几个顶点的线条。总之,我们谈论的是300,000个顶点。
为了尝试有效地做到这一点,我使用VBO来存储顶点数据。
我使用单个VBO存储所有行的所有顶点。然后我使用GLDrawElements
绘制整个VBO,传递一个索引数组,指定要绘制的索引,并利用primitiveRestart指定Linestrips开始/结束的位置(参见下面的代码)。
这不会像我希望的那样快速执行。我只能在10Hz左右渲染。我想也许是因为我传递了一大堆索引,每个索引都必须复制到每个渲染的GPU上。
我无法确定尝试提高效果的正确方向。着色器是否是正确的方法?有没有办法在渲染过程中不将索引数组写入GPU(DrawElements需要)。
任何有助于确定探索正确方向的帮助都将受到赞赏。
我是用C#使用openTK编写的。
//Enabled Primitive Restart
GL.Enable(EnableCap.PrimitiveRestart);
GL.PrimitiveRestartIndex(PrimitiveRestartIndex);
//Generate Linestrip data to buffer (array of vertices)
int[] FrameData = ScanBuffer.getBufferData();
//Create the buffer on the GPU
ScanBuffer.vbo_id = new int[1];
GL.GenBuffers(1, ScanBuffer.vbo_id);
//Buffer the data
GL.BindBuffer(BufferTarget.ArrayBuffer, ScanBuffer.vbo_id[0]);
GL.BufferData(BufferTarget.ArrayBuffer, new IntPtr(sizeof(int) * FrameData.Length), FrameData, BufferUsageHint.StaticDraw);
GL.VertexPointer(2, VertexPointerType.Int, 0, 0);
GL.EnableClientState(ArrayCap.VertexArray);
//Generate the vertex index array
uint[] IndexData = ScanBuffer.getIndexData();
//Draw the VBO
GL.BindBuffer(BufferTarget.ArrayBuffer, ScanBuffer.vbo_id[0]);
GL.VertexPointer(2, VertexPointerType.Int, 0, 0);
GL.DrawElements(BeginMode.LineStrip, IndexData.Length, DrawElementsType.UnsignedInt, IndexData);
我能够找到一个可行的解决方案,使用数组元素缓冲区来存储我的指标。我的性能提升了10倍。缓冲区和渲染命令如下所示。
vertexData = generateVertexData();
indexData = generateIndexData();
//Bind to the VAO
GL.BindVertexArray(vaoID[0]);
//Bind + Write Index Data to the Buffer
GL.BindBuffer(BufferTarget.ElementArrayBuffer, veoID[0]);
GL.BufferData(BufferTarget.ElementArrayBuffer, new IntPtr(sizeof(uint) * IndexData.Count()), IndexData.ToArray(), BufferUsageHint.StaticDraw);
//Bind + Write Scan Vertex Data to the Buffer
GL.BindBuffer(BufferTarget.ArrayBuffer, vboID[0]);
GL.BufferData(BufferTarget.ArrayBuffer, new IntPtr(sizeof(int) * VertexData.Count()), VertexData.ToArray(), BufferUsageHint.StaticDraw);
GL.VertexPointer(2, VertexPointerType.Int, 0, 0);
GL.EnableClientState(ArrayCap.VertexArray);
//Remove Binding
GL.BindVertexArray(0);
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
//Enabled Primitive Restart
GL.Enable(EnableCap.PrimitiveRestart);
GL.PrimitiveRestartIndex(PrimitiveRestartIndex);
GL.LineWidth(1);
GL.Color3(Color.Red);
GL.BindBuffer(BufferTarget.ArrayBuffer, vboID[0]);
GL.VertexPointer(2, VertexPointerType.Int, 0, 0);
//-Draw using a VEO-
GL.BindBuffer(BufferTarget.ElementArrayBuffer, veoID[0]);
GL.DrawElements(BeginMode.LineStrip, scanVertexBufferLength, DrawElementsType.UnsignedInt, 0);
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
GL.BindVertexArray(0);
GL.Disable(EnableCap.PrimitiveRestart);
答案 0 :(得分:0)
着色器最终可能有助于您的表现,但它们绝对不是此时的瓶颈。您的渲染工作量非常轻,如果正确完成,应该非常快。
首先,您对OpenGL的一些使用已经过时了。确保您遵循最新的文档和教程以避免这种情况。我知道进入一个新的领域来判断过时的是不是很难。如果可以的话,我建议从最近出版的OpenGL书开始。
现在开始使用您的代码。
GL.BufferData(BufferTarget.ArrayBuffer, new IntPtr(sizeof(int) * FrameData.Length), FrameData, BufferUsageHint.StaticDraw);
This tells OpenGL为顶点数据分配一个新的缓冲区,每次渲染几何体时都会这样做。这显然对性能非常不利。你想要做的只是分配缓冲区一次,或者至少只在它需要增长的时候。此调用还会将每个帧的300000个顶点位置上传到GPU,这也很昂贵。如果顶点位置没有改变,请上传一次,然后将缓冲区重新用于下一帧。如果位置确实发生了变化,请使用glBufferSubData()
上传新数据,而无需重新分配顶点缓冲区对象的内存。
GL.DrawElements(BeginMode.LineStrip, IndexData.Length, DrawElementsType.UnsignedInt, IndexData);
这是表现不佳的第二大罪魁祸首。在OpenGL"兼容模式"您必须使用的是glDrawElements()
,当没有缓冲区(id 0)绑定到IndexData
时(显然是您的情况),从最后一个参数(GL_ELEMENT_ARRAY_BUFFER
)读取顶点索引。实质上,这与前一段中描述的内容相同:分配新缓冲区并每帧上传数据。您应该创建另一个包含索引的缓冲区,将其绑定到GL_ELEMENT_ARRAY_BUFFER
并绘制。由于上述原因,这将避免内存分配和无用的数据传输。
最后,OpenGL是一个状态机。避免像GL.VertexPointer(2, VertexPointerType.Int, 0, 0);
这样的冗余调用。为了获得最佳性能,通常需要减少OpenGL函数调用的数量。