glTF是否在局部空间或模型空间中指定了骨矩阵? [英] glTF Are bone matrices specified in local or model space?

查看:77
本文介绍了glTF是否在局部空间或模型空间中指定了骨矩阵?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以官方文档说:

请注意,节点变换是节点相对于关节的局部变换,就像变换"部分中所述的glTF节点层次结构中的任何其他节点一样.

对于这里的节点和关节之间到底有什么区别,我有点模糊.

假设我可以将2混合在一起,请转到

它们不在同一位置.但是,如果您认为变换彼此独立(即,关节1的变换不会影响关节2),则关节1位于关节2上方,这至少与图中所示一致(尽管不一致)动画描述的内容,因为它是应该旋转的顶部骨骼,在这种配置下,它是将旋转的底部骨骼).

解决方案

哦,您在本教程中发现了各种缺陷.

通常是,节点"和接头"是相同的东西,除了它们分别进行索引.glTF 2.0使用数组索引号作为许多事物的标识,这种方法各有利弊.但是在这种情况下:

 节点:[{节点索引0},{节点索引1},{节点索引2}] 

其次:

 关节:[1,2] 

这意味着关节索引0由节点索引1表示,关节1由节点2表示.每个关节都是节点,但并非每个节点都是关节.当读取 JOINTS_0 数组时,这很重要,因为它们将是联合索引.但更重要的是,它是用这种方式编写的,因为 inverseBindMatrix 数组的长度与joints数组的长度相同,因此那里有1:1的映射保证.

让我们谈论一下jointMatrix.教程的实现部分具有以下伪代码:

  jointMatrix(j)=globalTransformOfNodeThatTheMeshIsAttachedTo ^ -1 *globalTransformOfJointNode(j)*inverseBindMatrixForJoint(j); 

但是,如果您被某些伪性烧伤了,这里是参见致电网站)

这是做什么的?我们需要"inverseWorldTransform".皮肤节点本身,以防某些愚蠢的艺术家将该节点移动到某个地方.这些绑定计算需要回到原点.当然,我们需要关节节点本身,实际骨骼的世界变换,该骨骼可能是动画的,或者可能已经由父关节的动画移动了,或者两者都有.最后,我们需要关节的inverseBindMatrix,如果关节没有将自己移离静止位置,它将抵消关节的位置.所有这些通常都在CPU上运行,每帧最多运行一次,或者至少在动画系统或其他外力导致某些关节移动时运行.

这里要解决的部分问题是:拥有蒙皮网格物体的节点可能在场景层次结构中具有特定位置,并且可能已移至某个位置.骨架的根可能是层次结构中的其他节点,也可能在其他位置.绘制蒙皮网格物体的顶点着色器试图将其绘制在蒙皮节点应指定的位置,而不是骨架根目录应在的位置.那是个问题.我们需要离开皮肤的位置,回到原点,然后下到关节的世界位置,然后将关节绑定到那里.来自顶点着色器的结果需要将皮肤放置在关节周围的关节位置,而不考虑蒙皮节点的位置.

如果您使用任何形式的变换将蒙皮的网格物体放置到场景根部以外的其他位置,则glTF验证程序会抱怨.使用皮肤时,所有位置信息都来自关节位置,而不是皮肤位置.通过在上面的计算中包括皮肤的世界逆向,可以丢弃皮肤的位置.因此,如果蒙皮节点位于原点上且未进行任何变换,则这是一个整体术语,您可以优化该方程.

但是,许多glTF模型的确在非根位置包含了外观,因此包含了蒙皮网格位置的反面,因为将从该位置调用顶点着色器.每个顶点都将到达关节实际所在的位置,然后通过关节的当前位置和关节的静止位置之间的差进一步移动自己.

So the official documentation says:

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.

I am a bit fuzzy as to what exactly the distinction is between node and joint here.

Assuming I can conflate the 2, go to the SimpleSkin exmaple int the models And look at the 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 ]
  } ],

joint 1 is the parent of joint 2. So if I treat that hierarchy like any other node hierarchy, the transform of node1 is to be applied to node 2 as well, given the values, at rest node 1 and node 2 rest in the same position.

But when you look at the diagrams for that example:

They are not in the same position. If, however you consider that the transforms are independent of each other (i.e that the transform of joint 1 does not affect joint 2), then joint 1 sits above joint 2, which is at minimum consistent with what the diagrams show (although not consistent with what the animation describes, since it's the top bone that should rotate and in this configuration it's the bottom one that will).

解决方案

Oh man, you're finding all kinds of flaws in the tutorials.

In general yes, "node" and "joint" are the same thing, except they are indexed separately. glTF 2.0 uses array index numbers as identification for many things, and there are pros and cons to that approach. But in this case:

nodes: [ { node index 0 }, { node index 1 }, { node index 2 } ]

Followed by:

joints: [ 1, 2 ]

This means that joint index 0 is represented by node index 1, and joint 1 by node 2. Every joint is a node, but not every node is a joint. This becomes important when reading the JOINTS_0 array, as those will be joint indices. But more importantly, it is written this way because the inverseBindMatrix array has the same length as the joints array, so there is a 1:1 mapping guarantee there.

Let's talk about the jointMatrix for a minute. The tutorial's implementation section has this bit of pseudocode:

jointMatrix(j) =
  globalTransformOfNodeThatTheMeshIsAttachedTo^-1 *
  globalTransformOfJointNode(j) *
  inverseBindMatrixForJoint(j);

But in case you're feeling a little burned by some of the pseudo-ness, here's the corresponding block of code in the sample viewer. It basically amounts to:

jointMatrix[j] = skinnedNode.inverseWorldTransform * 
                 jointNode.worldTransform * inverseBindMatrix[j]

The nodes' transformations are relative to their parents, but the inverseBindMatrix part is specified relative to this glTF model's world. So the host app needs to calculate the node's full transformation relative to glTF scene root, and the result of this calculation will change if any ancestor is animated. Likewise we need to know the inverse of the full world transform for a different node, one that holds the mesh with the skin on it (apparently mis-labeled "parentNode" in the sample viewer, it should be called "skinnedNode" or similar, see call site).

What does this do? We need the "inverseWorldTransform" of the skinned node itself, in case some foolish artist has moved that node somewhere. It needs to come back to the origin for these binding calculations. We of course need the world transform of the joint's node itself, the actual bone, which may be animated or may have been moved by a parent joint's animation, or both. And finally, we need the inverseBindMatrix for the joint, which if the joint hasn't moved itself away from the rest position, will cancel out the joint's position. This all typically runs on the CPU, at most once per frame, or at least whenever the animation system or other outside force causes some joints to move.

Part of the problem being solved here is this: The node that holds the skinned mesh may have a particular place in the scene hierarchy, and may have been moved somewhere. The root of the skeleton might be a different node in the hierarchy, and could be elsewhere. The vertex shader drawing the skinned mesh is trying to draw it where the skinned node says it should be, not where the skeleton root says it should be. That's a problem. We need to get out of the skin's location, get back to the origin, then go down to the joint's world location, and bind the joint there. The results coming out of the vertex shader need to place the skin around the joints, at the joint's location, regardless of where the skinned node thought it was.

The glTF Validator will complain if you place a skinned mesh somewhere other than the root of a scene, with any kind of transform. When a skin is in use, all of the location information comes from the joint locations, not the skin's location. The skin's location is thrown away by including its world inverse in the above calculations. So if the skinned node sits at the origin with no transform on it, that's a whole term you can optimize away from this equation.

But, many glTF models do include skins at non-root locations, so the inverse of the skinned mesh location is included because the vertex shader will be called from that location. Each vertex will make its way over to where the joints actually are, and then further move itself by the difference between the joint's current position and the joint's rest position.

这篇关于glTF是否在局部空间或模型空间中指定了骨矩阵?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆