An efficient way to control matrices in a graphics application using Texture Buffer objects (s) (OpenGL)

I am developing a small 3D engine using OpenGL and GLSL. I am currently using Texture Buffer Objects (TBOs) to store all my matrices (Proj, View, Model and Shadow Matrices). But I did some research on how best to handle matrices (I mean the most efficient way) in the graphics engine without any success. The goal is to store a maximum of matrices in a minimum number of TBOs and have a minimum of state changes and a minimum of exchanges between the GPU and client code (glBufferSubData).

I suggest 2 different methods (with their advantages and disadvantages):

Here is an example scene:

1 Camera (1 ProjMatrix, 1 ViewMatrix) 5 boxes (5 ModelMatrix)

Here is an example of a simple vertex shader that I use:

#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) The method I use: in my vertex shader, I use ModelViewProjMatrix (necessary for rasterization (gl_Position)), ModelViewMatrix (for calculating lighting) and ModelMatrix. Therefore, in order to avoid useless calculations in the vertex shader, I decided to save ModelViewProjMatrix, ModelViewMatrix and ModelMatrix for each node mesh nested in TBO as follows:

TBO = {[ModelViewProj_Box1] [ModelView_Box1] [Model_Box1] | [ModelViewProj_Box2] ...}

Advantages: I do not need to calculate the product Proj * View * Model (ModelViewProj for example) for each vertex shader (pre-computed matrices).

Disadvantages: if I move the camera, I need to update all ModelViewProj and ModelView models. So, a lot of information to update.

2) I thought of something else, I think it’s more efficient: save the projection matrix as soon as the presentation matrix and finally each model of the window of the node window will again look like this:

TBO = {[ProjMatrix] [ViewMatrix] [ModelMatrix_Box1] [ModelMatrix_Box2] ...}

So my vertex shader will look like this:

 #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; } } 

Advantages: TBO contains the exact number of matrices used. The update is strongly aimed (if I move the camera, I only update the view matrix, if I change the window size, I only update the projection matrix, and finally, if the object moves, only its model matrix will be updated).

Disadvantages: I need to calculate each vertex in the vertex shader of ModelViewProjMatrix. In addition, if the scene consists of a huge number of objects, each of which has a different model matrix, I probably need to create a new TBO. Therefore, I will lose information about the proj / view matrix, because I will not connect to the correct TBO, which will lead us to the third method.

3) Store the projection and view matrix in TBO and all other model matrices inside another or other TBO (s) as follows:

TBO_0 = {[ProjMatrix] [ViewMatrix]} TBO_1 = {[ModelMatrix_Box1] [ModelMatrix_Box2] ...}

What do you think of my 3 methods? Which one is best for you?

Thanks for your help!

+5
source share
1 answer

Solution 3 is what most engines do, except that they use uniform buffers (constant buffers) instead of texture buffers. In addition, they do not group all matrix models together in the same buffer at all, they are usually grouped according to the type of object (since the same objects are drawn immediately using instancing), and sometimes according to the refresh rate (objects that never are moved in the same buffer so that it never needs to be updated).

Also glBufferSubData can be quite slow; updating a buffer is often slower than just binding another, due to all the synchronization happening inside the driver. There is a very good chapter in a book about this that is freely available on the Internet called “OpenGL Insights: Asynchronous Buffer Translations” (Google to find it).

EDIT: The nvidia article you linked in the comments is very interesting. They recommend using glMultiDrawElements to immediately make some calls for the draw (this is the main trick, everything else is there because of this decision). This can significantly reduce the processor in the driver, but it also means that it is much more difficult to provide all the data needed to draw objects: you need to build / update large buffers for the values ​​of the matrices / materials, and you also need to use something like textures without bindings to be able to have different textures for each object. So, interesting, but harder.

And glMultiDrawElements is important if you want to draw a lot of different objects. Their examples have 68000-98000 different grids, which is really a lot. In a game, for example, you usually have many instances of the same objects, but only a few hundred different objects (maximum). In the end, it depends on what your 3D engine should do.

+2
source

All Articles