Opengl 3.0+:如何使用 Shader 有效地绘制分层(即链变换矩阵)网格?

Opengl 3.0+ : How to efficiently draw hierarchical (i.e. chain transform-matrix) meshes with Shader?

这是一个示例 3D 场景:-

网格 A 是网格 B 的父级。(父级类似于 3D 建模程序 Ex.Maya 或 Blender)

Mesh A和B的变换矩阵=MAMB.

在旧的Opengl中,Mesh A和Mesh B可以通过:-

glLoadIdentity();
glMulMatrix(MA);
MeshA.draw();
glMulMatrix(MB);
MeshB.draw();

在新的着色器Opengl 3.0+中,可以通过:-

shader.bind();
passToShader(MA);
MeshA.draw();
passToShader(MA*MB);
MeshB.draw();

着色器是:-

uniform mat4 multiplicationResult;
glVertex = M_multiplicationResult * meshPosition

MA在一个时间步长发生改变时:在旧方法中,只有MA需要重新计算。但是在新的方式中,使用着色器,整个 MA x MB 必须在 CPU 中重新计算。

在那些Mesh的层级(parenting)很高(Ex. 5 levels)和很多分支(Ex. one MeshA has many MeshB)的场景下问题变得严重,CPU有为每个相关的网格 E 重新计算整个 MA x MB x MC x MD x ME,即使只有单个 MA 被更改。

如何优化呢?或者这是要走的路?

我糟糕的解决方案:-

  1. 像这样在着色器中添加更多插槽:-

    uniform mat4 MA; 
    uniform mat4 MB; 
    uniform mat4 MC; 
    uniform mat4 MD; 
    uniform mat4 ME;
    glVertex = MA*MB*MC*MD*ME*meshPosition;
    

但是着色器永远不会知道多少 MX 就足够了。它是硬编码的,层次低,可维护性低,不支持更复杂的场景,浪费 GPU。

  1. 使用兼容性上下文 - 不是一个好的做法

But in the new way, using Shader, the whole MA x MB have to be recomputed in CPU.

你认为 glMultMatrix 在做什么?它也在计算 MA x MB。并且几乎可以肯定该计算发生在 CPU.

您想要的是一个类似于 OpenGL 矩阵堆栈的矩阵堆栈。所以...只需写一个。 OpenGL 所做的事情并没有什么神奇之处。您可以编写一个反映 OpenGL 矩阵操作的数据类型,然后在渲染时传递它。

或者,您可以只使用 C++ 堆栈:

void render(const matrix &parent)
{
  matrix me = parent * my_transform;
  passToShader(me);
  my_mesh.draw();
  for(each object)
    object.render(me);
}

好了,问题解决了。对象的每个子对象都会收到其父矩阵,用于计算自己的完整模型视图矩阵。


I hope to use something faster because they are "relatively-static" objects.

好的,让我们对此进行全面的性能分析。

我在上面发布的代码的一般 CPU 性能与 glMultMatrix 执行的矩阵乘法次数完全相同,因此您的代码现在和以前一样快(给或拿)。

那么,让我们考虑这样一种情况,即您将在 CPU 上进行的矩阵乘法运算的数量减到最少。现在,您正在为每个对象执行一次矩阵乘法。相反,我们不对每个对象进行矩阵乘法。

所以假设您的着色器有 4 个矩阵制服(无论是 4 元素矩阵还是只有 4 个单独的制服,都没有关系)。因此,您的最大堆栈深度被限制为 4,但现在不用担心了。

这样,您只需更改发生变化的矩阵。因此,如果父矩阵发生变化,则不必重新计算子矩阵。

好的...那又怎样?

您仍然需要将该子矩阵提供给着色器。所以你仍然要为改变程序统一状态付出代价。您仍在为每个对象向着色器上传 16 个浮点数。

不仅如此,请考虑您的顶点着色器现在必须做什么。它必须执行 4 vector/matrix 次乘法。它必须对 每个对象 每个顶点 执行此操作。毕竟,着色器不知道这些矩阵中哪些是空的,哪些不是。所以它必须假设它们都有数据,因此它必须乘以它们。

所以问题是,哪个更快:

  1. CPU

  2. 上每个对象的单个矩阵乘法
  3. GPU 上的每个顶点3 vector/matrix 次乘法(您至少需要执行一次)。