Linear Blend Skinning (LBS)线性混合蒙皮

LBS是CG的基础概念之一。

Linear Blend Skinning: linearly blend the results of the vertex transformed rigidly with each bone.

LBS:线性地混合顶点根据每个骨骼的刚性变形结果。

这个场景应用在哪里呢?
假如我们重建好一个人体,现在用全新的姿态对其进行驱动。本身姿态只会驱动骨架,但人体表面和衣服该怎么变换呢?
有以下公式:
V = Σ i = 1 n w i M i ′ M i − 1 V g = ( Σ i = 1 n w i T i ) V g V=\Sigma^n_{i=1}w_iM_i'M_i^{-1}V_g=(\Sigma^n_{i=1}w_iT_i)V_g V=Σi=1nwiMiMi1Vg=(Σi=1nwiTi)Vg
其中, Σ i = 1 n w i = 1 \Sigma^n_{i=1}w_i=1 Σi=1nwi=1

arm LBS

图1 LBS示意图

假设我们要确定一直手臂在弯折90度之后,表面的A点的移动位置。由图1可知,A点受两个关节点影响。
arm LBS

图2 弯折90度以后的手臂

手臂弯折后A点的位置就可以通过LBS来进行计算。注意看:首先,在手臂伸直状态(即图A),A点在胳膊关节点和手肘关节点两个位置都有一个坐标,这两个坐标通过图1中红色和绿色的向量(箭头)来表示。进行曲臂操作时,红色向量保持不动(因为胳膊没有弯曲),绿色向量会旋转90度(变成图2的样子),这时红色箭头和绿色箭头在蒙皮处分别有1个交点。我们会认为点A处于这两个交点之间,但具体坐标我们还需要进一步确定。这里就引进了Linear Blend Skinning方法,以确定点A在曲臂后的坐标。

由图1,2可知,点A在胳膊关节的坐标依旧是(3,1),在手肘关节的坐标从(-1,1)变为了(1,1),我们把坐标系都调整为胳膊关节为原点,那么手肘关节的坐标在变换后变成了(5,1)。那么,我们采用LBS公式,即加权平均来确定点A的新坐标:
0.8 ∗ ( 3 , 1 ) + 0.2 ∗ ( 5 , 1 ) = ( 3.4 , 1 ) 0.8*(3,1)+0.2*(5,1)=(3.4,1) 0.8(3,1)+0.2(5,1)=(3.4,1)
注意,这里的0.8和0.2分别是两个关节点对点A影响点权重。我们最后得到的点A坐标即为(3.4,1)。

那么,这个权重是怎么得到的呢?答:可以通过欧氏距离,也可以自己规定,现在也有很多基于学习的方法。


参考和致谢:https://homepages.inf.ed.ac.uk/tkomura/cav/presentation4_2020.pdf

### 蒙皮 (Skinning) 的实现方法 蒙皮是一种用于处理角色动画的技术,允许网格随着骨骼结构的变化而变形。这通常通过将网格顶点与多个骨骼关联来完成,并根据这些骨骼的变换调整顶点位置。 #### CPU 端的数据传输逻辑 在实现 GPU Skinning 技术时,CPU 端的数据传输逻辑至关重要。具体来说,需要将骨骼变换矩阵和网格数据传递给 GPU,在那里可以在顶点着色器中执行蒙皮计算[^3]。 #### 骨骼变换矩阵 为了使蒙皮生效,每帧都需要更新骨骼变换矩阵。这些矩阵描述了每个骨骼相对于其父节点的姿态变化。对于每一个影响特定顶点的骨骼,都会有一个对应的变换矩阵应用于该顶点的位置向量上。 #### 顶点混合算法 顶点融合(Vertex Blending),也称为线性混合蒙皮Linear Blend Skinning, LBS),是最常用的蒙皮技术之一。此过程涉及到按照预定义的影响因子组合来自不同骨骼的影响: \[ \text{new\_position} = \sum_{i=0}^{n}(w_i * M_i * v) \] 其中 \( w_i \) 是权重系数,\( M_i \) 表示第 i 个骨骼的变换矩阵,v 则代表原始顶点坐标[^4]。 #### 示例代码展示如何基于骨骼信息更新顶点位置 下面是一个简化版的例子,展示了怎样利用 C++ 和 GLSL 来实现基本的皮肤化渲染管道的一部分——即在 CPU 上构建好必要的输入之后交给 GPU 处理的部分: ```cpp // 假设我们已经有了一个包含所有关节转换后的最终世界空间下的变换矩阵数组 jointMatrices[] void updateJointTransforms(const std::vector<Matrix4>& jointsWorldSpace) { // 将所有的joint transformation matrix发送到shader里去. glUniformMatrix4fv(jointUniformLocation_, numJoints_, false, &jointsWorldSpace[0][0]); } ``` ```glsl #version 330 core layout(location = 0) in vec3 aPos; layout(location = 1) in vec3 aNormal; layout(location = 2) in vec2 aTexCoord; layout(location = 3) in ivec4 boneIds; // 指定哪些骨头作用于此顶点 layout(location = 4) in vec4 weights; // 对应于上述四个骨头的作用强度比例 uniform mat4 modelViewProjection; uniform mat4 inverseTransposeModelView; const int MAX_BONES = 100; uniform mat4 jointTransforms[MAX_BONES]; out VS_OUT { vec3 FragPos; vec3 Normal; vec2 TexCoord; } vs_out; void main() { mat4 BoneTransform = jointTransforms[boneIds[0]] * weights[0]; BoneTransform += jointTransforms[boneIds[1]] * weights[1]; BoneTransform += jointTransforms[boneIds[2]] * weights[2]; BoneTransform += jointTransforms[boneIds[3]] * weights[3]; vec4 LocalPosition = BoneTransform * vec4(aPos, 1.0); gl_Position = modelViewProjection * LocalPosition; vs_out.FragPos = vec3(LocalPosition); mat3 normalMatrix = transpose(inverse(mat3(BoneTransform))); vs_out.Normal = normalize(normalMatrix * aNormal); vs_out.TexCoord = aTexCoord; } ``` 这段GLSL片段显示了一个典型的顶点着色程序,它接收到了由应用程序上传送过来的一系列联合变换矩阵 `jointTransforms[]` ,以及当前正在被绘制的对象所使用的顶点属性列表。这里的关键在于使用加权求和的方式把几个可能会影响同一个顶点的不同骨骼的效果结合起来,从而得到新的顶点位置和其他相关信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木盏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值