我在组织各种矩阵乘法以实现场景图到我的WebGL场景时遇到了一些麻烦。
到目前为止,我曾经有三个矩阵, projectionMatrix , viewMatrix 和 modelMatrix ( normalMatrix 旁边),它们以相同的顺序相乘。我还有一个节点 - 对象,它基本上保存了绘制内容所需的所有信息。
当 projectionMatrix 和 viewMatrix 由drawScene
- 函数单独更新时, modelMatrix 等于每个节点 localMatrix ,其中 - 每次绘图调用 - 都设置为 identity ,然后按照我想要的方式进行转换。
节点存储在一个数组(nodeList
)中,当调用drawScene
- 函数时,我遍历此列表并告诉每个节点自己绘制。
现在我已经阅读了在www.webglfundamentals.org上为WebGL实现场景图的教程,并尝试将此功能添加到我的场景 -function中,但是本教程编写得非常好,我仍然对如何完成这项工作感到困惑。
遵循本教程,每个节点不仅必须具有 localMatrix ,还必须具有 worldMatrix 。然后,如果节点是 root 元素(即,当它没有父节点时),则它们的 localMatrix 和它们的 worldMatrix 是相同的,否则,如果是父节点,则子节点的 worldMatrix 通过将 localMatrix 与 worldMatrix相乘来计算他们的父节点,以便每个节点的 worldMatrix 在我之前的代码中曾经是 modelMatrix / localMatrix
也许我只是看不到森林的树木,但我问自己何时何地调用函数更新节点的世界矩阵以及何时设置矩阵身份,一劳永逸,只针对根节点或每个节点分别进行?
我的意思是,所述教程中提供的代码是通过递归遍历节点的所有子节点来完成的,但我的nodeList
- 数组并不反映节点和每个绘图调用的潜在层次关系,local-matrices被设置回 identity 。所以,它不能以这种方式工作,可以吗?
答案 0 :(得分:2)
修改强>
抱歉,我之前的回答是关于其他内容的,并且在教程中没有以这种方式实现(遗憾的是)。
- 每次绘图调用 - 都设置为标识,然后按照我想要的方式进行转换。
你为什么要回归身份?你不应该这样做。
但是我的
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
。你不与父母或孩子做任何事情。
第2阶段 - 找到世界位置
完成所有更新后,您需要重新计算所有worldMatrices。这意味着销毁旧worldMatrix
并创建新的rootNode.updateWorldMatrix()
。您可以使用worldMatrix
执行此操作。这将从上到下进行,并根据父worldMatrix和node localMatrix为树中的每个节点计算新的localMatrix
。它不会改变rootNode.draw()
。
第3阶段 - drawcall
现在我们回到旧的已知投影,视图和模型矩阵。执行类似worldMatrix
的操作,它是递归函数,将绘制层次结构中的每个节点。模型矩阵= mat4.translate()
。
关于强>
正如你所看到的那样,整个过程中没有setIdentity(worldmatrix可能会设置为被遗忘的身份,但我没有看到任何优势)。关于这一点的坏处是,如果你有充满树木的森林,树木不会移动,也不会移动地面,无论如何,每棵树都会重新计算它的世界位置。这可以通过扩充阶段1和2并使用标志扩展节点来防止。
编辑2
模型矩阵表示如何从0,0,0位置(将其理解为基点)进行模型转换(理解为平移,旋转和缩放)。
Scenegraph只做一件事,它将每个模型基点从静态(=静态位置,如0,0,0)更改为某个相对(另一个模型位置)。
每个模型仍然有自己的模型矩阵,它代表了从基点的转换。
但是我觉得你不能一遍又一遍地以不同的方式一次又一次地改变矩阵而不会有任何退缩,你呢?
这正是您可以而且应该做的! Matrix 4x4包含16个数字并具有预定义的操作。位置,旋转和比例是3个向量,每个向量有3个数字,这是9个数字,它们代表模型的当前状态(在3D中,在2D中我们有不同的变换)。我们将它们编译成一个mat4,所以mat4包含模型的当前状态,我们不再需要维护3个向量,所有都在矩阵中。
当您致电mat4.rotate()
,mat4.scale()
,mat4.multiply(mat4)
时,您需要转换模型。 tick()
不仅适用于一组转换,也适用于转换。
此外,矩阵设计为to not have its operations commutative! matA * matB!= matB * matA。这是非常有用的,例如,如果你先旋转相机然后翻译它,它将按照它正在看的方向移动。有时你不想要它,但大多数时候你想要它。
如果您不想要它,可以将矩阵设置回标识,将模型重置为默认位置,然后按照您想要的方式进行转换。
例如对于具有速度的汽车,每个{{1}}只在矩阵上进行小的三角形平移,它将沿着它前进的方向移动。如果玩家向右转,你只需要在右边进行小的三角形旋转然后进行平移。只需几行代码,您就可以轻松顺畅地移动汽车。
在场景图中,每个模型还有世界位置,它是所有先前父矩阵和模型矩阵之和的总和。它可以防止分别对每个模型进行所有乘法运算,因此非常深的模拟模型最需要进行大量的乘法运算。这很重要,想象一下你需要每秒进行60次乘法,所有这些都是用CPU完成的。
这不是我的想法,所有关于矩阵的东西都已经在GLSL本地化。图形卡准备与矩阵一起使用。
我的意思是,你已经认识到我对矩阵数学没那么多了
你不必,你只需要使用4个函数:translate,rotate,scale和mutliply,并知道模型状态由一个矩阵表示,这就是全部。背后的一切都是黑盒子。你不必知道如何从矩阵中获得模型距离,你只需要知道:一旦你做了modelviewprojectionmatrix * vertex,就会渲染模型。