glTF 指定的骨骼矩阵是局部的还是模型的space?
glTF Are bone matrices specified in local or model space?
所以官方文档说:
Note that the node transform is the local transform of the node relative to the joint, like any other node in the glTF node hierarchy as described in the Transformation section.
这里的node和joint到底有什么区别我有点搞不清楚
假设我可以将 2 混为一谈,转到 SimpleSkin
示例 int models 并查看 json:
"nodes" : [ {
"skin" : 0,
"mesh" : 0
}, {
"children" : [ 2 ],
"translation" : [ 0.0, 1.0, 0.0 ]
}, {
"rotation" : [ 0.0, 0.0, 0.0, 1.0 ]
} ],
"skins" : [ {
"inverseBindMatrices" : 4,
"joints" : [ 1, 2 ]
} ],
关节 1 是关节 2 的父节点。因此,如果我像对待任何其他节点层次结构一样对待该层次结构,节点 1 的变换也将应用于节点 2,给定值,静态节点 1 和节点2个在同一个位置休息。
但是当您查看该示例的图表时:
他们不在同一个位置。但是,如果您认为变换彼此独立(即关节 1 的变换不影响关节 2),则关节 1 位于关节 2 上方,这至少与图表显示的一致(尽管不一致)根据动画描述的内容,因为它是应该旋转的顶部骨骼,而在此配置中它是底部的骨骼)。
哦,伙计,你在教程中发现了各种缺陷。
一般来说,是的,“节点”和“联合”是同一个东西,只是它们分别被索引。 glTF 2.0 使用数组索引号作为许多事物的标识,这种方法有利有弊。但在这种情况下:
nodes: [ { node index 0 }, { node index 1 }, { node index 2 } ]
其次是:
joints: [ 1, 2 ]
这意味着关节索引0由节点索引1表示,关节1由节点2表示。每个关节都是一个节点,但不是每个节点都是一个关节。这在读取 JOINTS_0
数组时变得很重要,因为它们将是联合索引。但更重要的是,这样写是因为inverseBindMatrix
数组与关节数组的长度相同,所以那里有1:1映射保证。
我们来谈谈 jointMatrix。 tutorial's implementation section 有这段伪代码:
jointMatrix(j) =
globalTransformOfNodeThatTheMeshIsAttachedTo^-1 *
globalTransformOfJointNode(j) *
inverseBindMatrixForJoint(j);
但是,如果您对某些 pseudo-ness 感到有点恼火,这里是 corresponding block of code in the sample viewer。它基本上等于:
jointMatrix[j] = skinnedNode.inverseWorldTransform *
jointNode.worldTransform * inverseBindMatrix[j]
节点的变换是相对于它们的父节点的,但是 inverseBindMatrix
部分是相对于这个 glTF 模型的世界指定的。所以宿主应用程序需要计算节点相对于 glTF 场景根的完整变换,如果任何祖先被动画化,这个计算的结果将会改变。同样,我们需要知道一个不同节点的完整世界变换的逆向,一个节点持有带有皮肤的网格(显然 mis-labeled 样本查看器中的“parentNode”,它应该被称为“skinnedNode”或相似,see call site).
这是做什么的?我们需要蒙皮节点本身的“inverseWorldTransform”,以防某些愚蠢的艺术家将该节点移动到某处。这些绑定计算需要回到原点。我们当然需要关节节点本身的世界变换,实际的骨骼,它可能是动画的或者可能已经被父关节的动画移动,或者两者兼而有之。最后,我们需要关节的 inverseBindMatrix,如果关节没有将自身从静止位置移开,它将抵消关节的位置。这一切通常都在 CPU 上运行,每帧最多一次,或者至少在动画系统或其他外力导致某些关节移动时运行。
这里要解决的部分问题是:保存蒙皮网格的节点可能在场景层次结构中有一个特定的位置,并且可能已移动到某个地方。骨架的根可能是层次结构中的不同节点,也可能在其他地方。绘制蒙皮网格的顶点着色器试图将其绘制在蒙皮节点指定的位置,而不是骨架根指定的位置。那是个问题。我们需要离开皮肤的位置,回到原点,然后下到关节的世界位置,并在那里绑定关节。顶点着色器的结果需要在关节周围放置皮肤,在关节的位置,无论蒙皮节点认为它在哪里。
如果您使用任何类型的变换将蒙皮网格放置在场景根以外的某个位置,glTF 验证器将会报错。使用皮肤时,所有位置信息都来自关节位置,而不是皮肤的位置。皮肤的位置通过在上述计算中包括它的世界倒数而被丢弃。因此,如果蒙皮节点位于原点且其上没有变换,则这是一个完整的项,您可以从该等式中进行优化。
但是,许多 glTF 模型确实在 non-root 位置包含蒙皮,因此包含蒙皮网格位置的倒数,因为将从该位置调用顶点着色器。每个顶点都会移动到关节实际所在的位置,然后根据关节的当前位置和关节的静止位置之间的差异进一步移动自身。
只是为了补充答案,这就是骨骼的实际外观:
所以官方文档说:
Note that the node transform is the local transform of the node relative to the joint, like any other node in the glTF node hierarchy as described in the Transformation section.
这里的node和joint到底有什么区别我有点搞不清楚
假设我可以将 2 混为一谈,转到 SimpleSkin
示例 int models 并查看 json:
"nodes" : [ {
"skin" : 0,
"mesh" : 0
}, {
"children" : [ 2 ],
"translation" : [ 0.0, 1.0, 0.0 ]
}, {
"rotation" : [ 0.0, 0.0, 0.0, 1.0 ]
} ],
"skins" : [ {
"inverseBindMatrices" : 4,
"joints" : [ 1, 2 ]
} ],
关节 1 是关节 2 的父节点。因此,如果我像对待任何其他节点层次结构一样对待该层次结构,节点 1 的变换也将应用于节点 2,给定值,静态节点 1 和节点2个在同一个位置休息。
但是当您查看该示例的图表时:
他们不在同一个位置。但是,如果您认为变换彼此独立(即关节 1 的变换不影响关节 2),则关节 1 位于关节 2 上方,这至少与图表显示的一致(尽管不一致)根据动画描述的内容,因为它是应该旋转的顶部骨骼,而在此配置中它是底部的骨骼)。
哦,伙计,你在教程中发现了各种缺陷。
一般来说,是的,“节点”和“联合”是同一个东西,只是它们分别被索引。 glTF 2.0 使用数组索引号作为许多事物的标识,这种方法有利有弊。但在这种情况下:
nodes: [ { node index 0 }, { node index 1 }, { node index 2 } ]
其次是:
joints: [ 1, 2 ]
这意味着关节索引0由节点索引1表示,关节1由节点2表示。每个关节都是一个节点,但不是每个节点都是一个关节。这在读取 JOINTS_0
数组时变得很重要,因为它们将是联合索引。但更重要的是,这样写是因为inverseBindMatrix
数组与关节数组的长度相同,所以那里有1:1映射保证。
我们来谈谈 jointMatrix。 tutorial's implementation section 有这段伪代码:
jointMatrix(j) =
globalTransformOfNodeThatTheMeshIsAttachedTo^-1 *
globalTransformOfJointNode(j) *
inverseBindMatrixForJoint(j);
但是,如果您对某些 pseudo-ness 感到有点恼火,这里是 corresponding block of code in the sample viewer。它基本上等于:
jointMatrix[j] = skinnedNode.inverseWorldTransform *
jointNode.worldTransform * inverseBindMatrix[j]
节点的变换是相对于它们的父节点的,但是 inverseBindMatrix
部分是相对于这个 glTF 模型的世界指定的。所以宿主应用程序需要计算节点相对于 glTF 场景根的完整变换,如果任何祖先被动画化,这个计算的结果将会改变。同样,我们需要知道一个不同节点的完整世界变换的逆向,一个节点持有带有皮肤的网格(显然 mis-labeled 样本查看器中的“parentNode”,它应该被称为“skinnedNode”或相似,see call site).
这是做什么的?我们需要蒙皮节点本身的“inverseWorldTransform”,以防某些愚蠢的艺术家将该节点移动到某处。这些绑定计算需要回到原点。我们当然需要关节节点本身的世界变换,实际的骨骼,它可能是动画的或者可能已经被父关节的动画移动,或者两者兼而有之。最后,我们需要关节的 inverseBindMatrix,如果关节没有将自身从静止位置移开,它将抵消关节的位置。这一切通常都在 CPU 上运行,每帧最多一次,或者至少在动画系统或其他外力导致某些关节移动时运行。
这里要解决的部分问题是:保存蒙皮网格的节点可能在场景层次结构中有一个特定的位置,并且可能已移动到某个地方。骨架的根可能是层次结构中的不同节点,也可能在其他地方。绘制蒙皮网格的顶点着色器试图将其绘制在蒙皮节点指定的位置,而不是骨架根指定的位置。那是个问题。我们需要离开皮肤的位置,回到原点,然后下到关节的世界位置,并在那里绑定关节。顶点着色器的结果需要在关节周围放置皮肤,在关节的位置,无论蒙皮节点认为它在哪里。
如果您使用任何类型的变换将蒙皮网格放置在场景根以外的某个位置,glTF 验证器将会报错。使用皮肤时,所有位置信息都来自关节位置,而不是皮肤的位置。皮肤的位置通过在上述计算中包括它的世界倒数而被丢弃。因此,如果蒙皮节点位于原点且其上没有变换,则这是一个完整的项,您可以从该等式中进行优化。
但是,许多 glTF 模型确实在 non-root 位置包含蒙皮,因此包含蒙皮网格位置的倒数,因为将从该位置调用顶点着色器。每个顶点都会移动到关节实际所在的位置,然后根据关节的当前位置和关节的静止位置之间的差异进一步移动自身。
只是为了补充答案,这就是骨骼的实际外观: