我在组织各种矩阵乘法以实现WebGL场景的场景图时遇到一些麻烦。

到目前为止,我以前有三个矩阵,projectionMatrix,viewMatrix和modelMatrix(不包括normalMatrix),它们以相同的顺序相乘。我也有一个节点对象,它基本上包含绘制某物所需的所有信息。

通过drawScene函数分别更新projectionMatrix和viewMatrix时,modelMatrix等于每个节点localMatrix,在每次绘制调用时将其设置为identity,然后按照我想要的方式进行转换。

节点存储在数组(nodeList)中,当调用drawScene函数时,我遍历此列表并告诉每个节点自己绘制。

现在,我已经阅读了有关在www.webglfundamentals.org上为WebGL实现场景图的教程,并试图将该功能添加到我的场景功能中,但是尽管本教程写得很好,但我仍然对如何获得此功能感到困惑完成。

按照本教程,每个节点不仅必须具有localMatrix,而且还必须具有worldMatrix。然后,如果节点是根元素(即,当它没有父节点时),则它们的localMatrix和worldMatrix相同,否则,如果有父节点,则通过乘以计算子节点的worldMatrix它们的localMatrix及其父节点的worldMatrix,因此每个节点的worldMatrix就是我以前的代码中曾经的modelMatrix / localMatrix。

也许我只是看不见树木的森林,但是我问自己,何时何地调用该函数来更新节点的世界矩阵以及何时将矩阵重新设置为恒等式,一次是为了根节点还是每个节点分别?

我的意思是,所述教程中提供的代码通过递归遍历节点的所有子节点来完成,但是我的nodeList数组未反映节点的潜在层次关系以及每个绘图调用,局部矩阵重新设置为身份。因此,它不能以这种方式工作,可以吗?

最佳答案

编辑

抱歉,我先前的回答是关于其他问题,在教程中(不幸地)没有以这种方式实现。


  每次进行绘图调用时,都将其设置为标识,然后更改我想要的方式。


你为什么要设置任何东西来恢复身份?你不应该那样做。


  但是我的nodeList数组不能反映节点的潜在层次关系,并且在每次绘制调用时,局部矩阵都重新设置为恒等。因此,它不能以这种方式工作,可以吗?


场景图的目的是简化并避免其他数学运算。这需要树层次结构中的节点。

初始化

以免有教程中的节点:

var node = {
   localMatrix: ...,  // the "local" matrix for this node
   worldMatrix: ...,  // the "world" matrix for this node
   children: [],      // array of children
   thingToDraw: ??,   // thing to draw at this node
};


然后,您有两件事,整个场景的根节点和重要元素所在的nodeList(将来可能会转换)。

var rootNode = new Node();
var nodeList = [];


现在将所有内容放入节点层次结构中,所有节点都是rootNode的一部分。而将来可能转换的这些文件保存在nodeList中。

每个tick()重复阶段

阶段1-更新

更新所需的所有元素的位置,这意味着从nodeList中选择内容并转换localMatrix。您不与父母或子女做任何事情。

第二阶段-寻找世界位置

完成所有更新后,您需要重新计算所有worldMatrices。这意味着销毁旧的worldMatrix并创建新的。您可以通过rootNode.updateWorldMatrix()执行此操作。这将自上而下,并基于父worldMatrix和节点localMatrix为树中的每个节点计算新的worldMatrix。它不会更改localMatrix

第三阶段-抽奖

现在我们回到已知的旧投影,视图和模型矩阵。执行类似rootNode.draw()的操作,该功能是递归函数,它将按层次结构绘制每个节点。模型矩阵= worldMatrix

关于

如您所见,在整个过程中没有setIdentity(worldmatrix可能被设置为可能被存储的身份,但是我看不出有任何好处)。不好的事情是,如果您的森林到处都是树木,树木既不动也不动,那么每棵树无论如何都会重新计算其世界位置。这可以通过增加阶段1和2并扩展带有标志的节点来防止。

编辑2

模型矩阵表示如何从0,0,0位置(理解为基点)对模型进行变换(理解为平移,旋转和缩放)。

Scenegraph只做一件事,它将每个模型的基点从静态(=静态位置,如0,0,0)更改为相对(另一个模型位置)。

每个模型仍然具有自己的模型矩阵,这些矩阵表示其从基点的转换。


  但是我认为您不能在没有任何挫折的情况下一次又一次地反复变换矩阵。


这正是您可以并且应该做的!矩阵4x4包含16个数字,并具有预定义的操作。位置,旋转和比例是3个向量,每个向量具有3个数字,即9个数字,它们代表模型的当前状态(在3D,2D中,我们具有不同的变换)。我们将它们组合为一个mat4,因此mat4包含模型的当前状态,我们不再需要维护3个向量,所有向量都在矩阵中。



调用mat4.translate()mat4.rotate()mat4.scale()时,您将转换模型。 mat4.multiply(mat4)不仅适用于一个转换,还适用于一组转换。

另外,矩阵被设计为to not have its operations commutative!
matA * matB!= matB * matA。例如,这对您很有帮助,如果您先旋转相机然后平移它,则它将朝所看方向移动。有时您不想要它,但是大多数时候您想要它。

万一您不想要它,可以将矩阵设置为恒等,这会将模型重置为默认位置,然后按照您想要的方式进行变换。

例如,对于有速度的汽车,对于每个tick(),您只需对矩阵进行较小的增量平移,它将沿其前进方向移动。而且,如果玩家向右转,您只需向右旋转较小的增量,然后平移即可。只需几行代码,您就可以轻松顺畅地移动汽车。

在场景图中,每个模型都有一个世界位置,这是所有以前的父矩阵和模型矩阵之和。它阻止了对每个模型分别进行所有乘法,因此非常深的模拟模型确实需要大量乘法。这很重要,假设您需要每秒进行60次所有乘法,而所有这些操作都需要使用CPU。

这不是我的想法,有关矩阵的所有内容已经在GLSL中原生。图形卡准备与矩阵一起使用。


  我的意思是,您已经意识到我对矩阵数学的了解不多


您不必这样做,只需使用4个功能:平移,旋转,缩放和mutiply,并知道模型状态由一个矩阵表示,仅此而已。后面的所有内容都必须放在黑匣子中。您不必知道如何从矩阵获取模型距离,只需知道:一旦执行了modelviewprojectionmatrix *顶点,就可以渲染模型。

10-06 12:22