使用纹理缓冲区对象在图形应用程序中管理矩阵的有效方法(OpenGL)
我正在用OpenGL和GLSL开发一个小的3D引擎。我目前使用纹理缓冲区对象(TBO)来存储我的所有矩阵(Proj、View、Model和Shadow矩阵)。但是我做了一些关于在图形引擎中处理矩阵的最佳方法(我指的是最有效的方法)的研究,但没有成功。目标是将最多的矩阵存储到最少数量的TBO中,并在GPU和客户机代码(glBufferSubData)之间发生最少的状态更改和最少的交换 我提出了两种不同的方法(及其优缺点): 下面是一个场景示例: 1个摄像头(1个ProjMatrix,1个ViewMatrix) 5个盒子(5个模型矩阵) 下面是我使用的一个简单顶点着色器的示例:使用纹理缓冲区对象在图形应用程序中管理矩阵的有效方法(OpenGL),opengl,matrix,glsl,shader,vertex-shader,Opengl,Matrix,Glsl,Shader,Vertex Shader,我正在用OpenGL和GLSL开发一个小的3D引擎。我目前使用纹理缓冲区对象(TBO)来存储我的所有矩阵(Proj、View、Model和Shadow矩阵)。但是我做了一些关于在图形引擎中处理矩阵的最佳方法(我指的是最有效的方法)的研究,但没有成功。目标是将最多的矩阵存储到最少数量的TBO中,并在GPU和客户机代码(glBufferSubData)之间发生最少的状态更改和最少的交换 我提出了两种不同的方法(及其优缺点): 下面是一个场景示例: 1个摄像头(1个ProjMatrix,1个ViewM
#version 400
/*
** Vertex attributes.
*/
layout (location = 0) in vec4 VertexPosition;
layout (location = 1) in vec2 VertexTexture;
/*
** Uniform matrix buffer.
*/
uniform samplerBuffer matrixBuffer;
/*
** Matrix buffer offset.
*/
uniform int MatrixBufferOffset;
/*
** Output variables.
*/
out vec2 TexCoords;
/*
** Returns matrix4x4 from texture cache.
*/
mat4 Get_Matrix(int offset)
{
return (mat4(texelFetch(
matrixBuffer, offset), texelFetch(
matrixBuffer, offset + 1), texelFetch(matrixBuffer, offset + 2),
texelFetch(matrixBuffer, offset + 3)));
}
/*
** Vertex shader entry point.
*/
void main(void)
{
TexCoords = VertexTexture;
{
mat4 ModelViewProjMatrix = Get_Matrix(
MatrixBufferOffset);
gl_Position = ModelViewProjMatrix * VertexPosition;
}
}
1) 我当前使用的方法:在顶点着色器中,我使用ModelViewProjMatrix(光栅化(gl_位置)所需)、ModelViewMatrix(用于照明计算)和ModelMatrix。因此,为了避免顶点着色器中的无用计算,我决定为TBO中内联的每个网格节点存储ModelViewProjMatrix、ModelViewMatrix和ModelMatrix,如下所示:
TBO={[ModelViewProj_Box1][ModelView_Box1][Model_Box1]|[ModelViewProj_Box2]…}
优点:我不需要为每个顶点着色器(矩阵是预先计算的)计算product Proj*View*模型(例如ModelViewProj)
缺点:如果移动相机,则需要更新所有ModelViewProj和ModelView矩阵。所以,有很多信息需要更新
2) 我考虑了另一种方法,我认为更有效:存储一次投影矩阵,一次视图矩阵,最后再存储一次每个长方体场景节点模型矩阵,方法如下:
TBO={[ProjMatrix][ViewMatrix][ModelMatrix_Box1][ModelMatrix_Box2]…}
因此,我的顶点着色器将如下所示:
#version 400
/*
** Vertex attributes.
*/
layout (location = 0) in vec4 VertexPosition;
layout (location = 1) in vec2 VertexTexture;
/*
** Uniform matrix buffer.
*/
uniform samplerBuffer matrixBuffer;
/*
** Matrix buffer offset.
*/
uniform int MatrixBufferOffset;
/*
** Output variables.
*/
out vec2 TexCoords;
/*
** Returns matrix4x4 from texture cache.
*/
mat4 Get_Matrix(int offset)
{
return (mat4(texelFetch(
matrixBuffer, offset), texelFetch(
matrixBuffer, offset + 1), texelFetch(matrixBuffer, offset + 2),
texelFetch(matrixBuffer, offset + 3)));
}
/*
** Vertex shader entry point.
*/
void main(void)
{
TexCoords = VertexTexture;
{
mat4 ProjMatrix = Get_Matrix(MatrixBufferOffset);
mat4 ViewMatrix = Get_Matrix(MatrixBufferOffset + 4);
mat4 ModelMatrix = Get_Matrix(MatrixBufferOffset + 8);
gl_Position = ProjMatrix * ViewMatrix * ModelMatrix * VertexPosition;
}
}
优点:TBO包含所用矩阵的确切数量。更新具有很强的针对性(如果移动摄影机,则仅更新视图矩阵;如果调整窗口大小,则仅更新投影矩阵;最后,如果对象仅移动,则将更新其模型矩阵)
缺点:我需要计算ModelViewProjMatrix中顶点着色器中的每个顶点。另外,如果场景由大量对象组成,并且每个对象都拥有不同的模型矩阵,我可能需要创建一个新的TBO。因此,我将丢失proj/view矩阵信息,因为我无法连接到正确的TBO,这就引出了我的第三种方法
3) 将投影和视图矩阵存储在TBO中,并将所有其他模型矩阵存储在另一个或其他TBO中,如下所示:
TBO_0={[ProjMatrix][ViewMatrix]}
TBO_1={[ModelMatrix_Box1][ModelMatrix_Box2]…}
你觉得我的三种方法怎么样?哪一个最适合你
非常感谢您的帮助 解决方案3是大多数引擎所做的,只是它们使用统一缓冲区(常量缓冲区)而不是纹理缓冲区。此外,它们通常不会在同一缓冲区中将所有模型矩阵分组在一起,它们通常按对象类型分组(因为使用实例一次绘制相同的对象),有时按更新频率分组(从不移动的对象位于同一缓冲区中,因此不需要更新) 此外,子数据可能非常慢;更新缓冲区通常比绑定不同的缓冲区慢,因为所有同步都发生在驱动程序内部。关于这一点,有一本非常好的书,在互联网上免费提供,叫做“OpenGL洞察:异步缓冲区传输”(谷歌搜索) 编辑:你在评论中链接的内容非常有趣。他们建议使用glmultidrawerelements一次进行几个draw调用(这是主要的技巧,其他一切都是因为这个决定)。这可以大大减少驱动程序中的CPU工作,但这也意味着提供绘制对象所需的所有数据要复杂得多:必须为矩阵/材质值构建/更新更大的缓冲区,并且还需要使用无绑定纹理之类的东西,以便能够为每个对象创建不同的纹理。所以,很有趣,但更复杂
只有当你想画很多不同的物体时,多重元素才是重要的。他们的例子有68000-98000个不同的网格,这真是太多了。例如,在游戏中,通常有许多相同对象的实例,但只有几百个不同对象(最多)。最后,这取决于3D引擎需要渲染什么。为什么要使用TBO而不是统一的缓冲区?因为尺寸限制?你好,杰里米,谢谢你的回复。我决定使用TBO而不是UBO(仅用于材质和灯光参数),因为我读了NVIDIA关于这个主题的一篇文章。也许你已经读过了。这是链接(您可以从第31张幻灯片访问TBO矩阵存储)。你能告诉我你对这篇文章的看法吗?提前谢谢!在幻灯片34中,他们说TBO用于存储大型/随机访问数据,UBO仅用于频繁更改,而不用于分散访问。这到底是什么意思?我们可以把矩阵看作大数据吗?那么在阅读本文之后,TBO和UBO之间为您存储矩阵的最佳方式是什么?您可以将您的选择锁定在UBO上:)?TBO更适合于大数据,因为它没有大小限制(与限制为64KB的UBO不同)。当着色器的两个实例不读取UBO中的同一位置时,会发生不同的访问。例如,对于片段着色器,nvidia GPU通常运行同一着色器的32个实例,一次着色32个像素。所有这些实例同时运行相同的指令。如果它们中的一个碰巧以与其他缓冲区不同的偏移量读取缓冲区,则这是一种不同的访问。TBO可能在这方面做得更好