Skip to content

Instantly share code, notes, and snippets.

@tylearymf
Created April 15, 2022 13:33
Show Gist options
  • Save tylearymf/a39fd7e0b5f5c39d0ef29c2683d69a75 to your computer and use it in GitHub Desktop.
Save tylearymf/a39fd7e0b5f5c39d0ef29c2683d69a75 to your computer and use it in GitHub Desktop.
Unity Mesh bindPoses 详细说明
/**
* bindPoses 详细说明
* 转自 https://forum.unity.com/threads/some-explanations-on-bindposes.86185/
*
* 翻译如下:
* bindPose 矩阵是允许将网格的原始顶点(在局部空间下)转换到世界空间
* 然后到每个骨骼的局部空间,之后每隔骨骼的动画可以应用到有问题的顶点(受权重值影响)。
*
* 网格的 bindPose 将顶点从局部空间转换到世界空间,
* 然后每个骨骼的 bindPose的逆变换矩阵 将网格顶点从世界空间转换到骨骼的局部空间,
* 一旦进入骨骼的局部空间,骨骼的当前动画变换矩阵用于变换(变形)顶点的位置
* 在考虑了所有的骨骼的影响之后,顶点在其最终变形位置的世界空间中结束。换句话说,
* 这些 bindPose 矩阵将顶点在其局部网格空间中的位置与顶点相对于每个骨骼的局部坐标系的相同位置相关联
*
* 网格的 bindPose 矩阵将其顶点转换到世界空间,到达绑定时的位置
* 每个骨骼的 bindPose 矩阵将骨骼从局部空间下转换到绑定时的世界空间
*
* 自己的理解如下:
* 首先,有两个 bindPose,对应骨骼的和网格的
* 1、网格的 bindPose 是将它局部空间下的顶点转换到世界空间
* 2、骨骼的 bindPose 是将它局部空间下的骨骼转换到世界空间
* 然后,要想想怎么将网格的顶点和骨骼关联起来,流程如下:
* 1、网格的顶点通过 网格的bindPose 转换到世界空间
* 2、骨骼的bindPose取逆矩阵,然后转换到骨骼的局部空间
* 这样就将网格的顶点与骨骼关联起来了
*
* 再放到 Unity下来说下 bindPose
* 首先 Unity下的mesh的bindposes其实就是 骨骼的bindPose的逆矩阵
* 所以我们得到 bindposes[i] = bones[i].worldToLocalMatrix(这里就是将骨骼从世界空间转换到局部空间)
* 但是因为我们的骨骼一般会挂载在某个根节点上,用来整体的拖拽移动、旋转、缩放等等操作,
* 所以我们还需要去乘以 根节点的局部转世界矩阵,也就是 最终的bindposes 如下:
* bindposes[i] = bones[i].worldToLocalMatrix * root.localToWorldMatrix
*
**/
void Start()
{
var go = new GameObject("Renderer");
var renderer = go.AddComponent<SkinnedMeshRenderer>();
go.transform.parent = transform;
go.transform.localPosition = Vector3.zero;
go.transform.localRotation = Quaternion.identity;
go.transform.localScale = Vector3.one;
// 创建mesh
var mesh = new Mesh();
mesh.vertices = new Vector3[]
{
new Vector3(-1, 0, 0),
new Vector3(1, 0, 0),
new Vector3(-1, 5, 0),
new Vector3(1, 5, 0)
};
mesh.uv = new Vector2[]
{
new Vector2(0, 0),
new Vector2(1, 0),
new Vector2(0, 1),
new Vector2(1, 1)
};
mesh.triangles = new int[]
{
0, 1, 2,
1, 3, 2,
2, 1, 0,
2, 3, 1
};
renderer.material = new Material(Shader.Find("Diffuse"));
var weights = new BoneWeight[4];
weights[0].boneIndex0 = 1;
weights[0].weight0 = 1;
weights[1].boneIndex0 = 1;
weights[1].weight0 = 1;
weights[2].boneIndex0 = 0;
weights[2].weight0 = 1;
weights[3].boneIndex0 = 0;
weights[3].weight0 = 1;
mesh.boneWeights = weights;
var bones = new Transform[2];
var bindPoses = new Matrix4x4[2];
// 创建骨骼
bones[0] = new GameObject("Bone_Upper").transform;
bones[1] = new GameObject("Bone_Lower").transform;
bones[0].parent = transform;
bones[1].parent = transform;
// 设置 upper 骨骼位置
bones[0].localPosition = Vector3.up * 5;
bones[0].localRotation = Quaternion.identity;
bones[0].localScale = Vector3.one;
// 设置 lower 骨骼位置
bones[1].localPosition = Vector3.zero;
bones[1].localRotation = Quaternion.identity;
bones[1].localScale = Vector3.one;
// bindPoses 是骨骼的逆矩阵
// 先将 bones[i] 转换到 根节点的局部空间下,即:bones[i].worldToLocalMatrix
// 然后保持 bones[i] 和 transform 的相对关系,但是要转换到 根节点的世界空间下,即 * tranform.localToWorldMatrix
// 此时我们的 bones[i] 的 bindpose 就已经设置好了,可以进行 平移/旋转/缩放,同时对骨骼的根节点也生效
for (int i = 0; i < 2; i++)
bindPoses[i] = bones[i].worldToLocalMatrix * transform.localToWorldMatrix;
mesh.bindposes = bindPoses;
mesh.RecalculateNormals();
mesh.RecalculateBounds();
renderer.bones = bones;
renderer.sharedMesh = mesh;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment