Skip to content

捏脸系统设计 ​

捏脸的本质是把「自由建模」这个需要专业 3D 知识的操作,降维成十几个滑块——用户拖的是「脸宽」「眼角角度」,系统背后算的是一组 BlendShape 权重的组合插值。

面部参数15 个
单参数映射1–3 个 BS
移动端调整<2ms

背景 ​

虚拟形象要让普通用户捏出「像自己」或者「想要」的脸。专业 3D 软件的自由建模路线直接排除——用户没有顶点级操作的能力,移动端也没有那个交互空间。可行路线只有参数化:美术预先做好一组 BlendShape(BS,形态键),每个 BS 表达一个面部形变方向,系统把用户语义的参数(脸宽、眼距)映射成若干 BS 权重的组合。

捏脸设计

问题 ​

问题表现设计约束
参数语义 ≠ BS 语义「脸宽」不是一个 BS 能表达的一个参数要映射多个 BS
组合冲突极端参数组合下脸型崩坏权重范围要钳制在合理区间
移动端算力拖动滑块要实时预览每帧 BS 计算必须毫秒级
结果不可复现用户捏完换设备没了参数组合要可序列化

方案 ​

mermaid
flowchart LR
  A["用户拖滑块<br>脸宽 = 0.7"] --> B["参数 → BS 映射表"]
  B --> C["Face_Width_X<br>Lerp(-50, 50)"]
  B --> D["Cheek_Width<br>Lerp(-20, 30) × 0.6"]
  C --> E["BS 权重合成"]
  D --> E
  E --> F["GPU 顶点位移<br>实时预览"]

设计要点是把「美术语义」和「用户语义」分离:BS 是美术做的形变基元,参数是用户能理解的概念,中间的映射表是策划/美术可调的数据,不是写死的代码。

关键实现 ​

面部参数表 ​

分类参数BS 控制
脸型脸宽、脸长、下巴尖度Face_Width, Face_Length, Jaw_Shape
眼睛眼大、眼距、眼角角度Eye_Size, Eye_Space, Eye_Angle
鼻子鼻宽、鼻高、鼻尖Nose_Width, Nose_Height, Nose_Tip
嘴巴嘴宽、嘴厚、嘴角Mouth_Width, Lip_Thick, Mouth_Corner
眉毛眉高、眉距、眉角Brow_Height, Brow_Space, Brow_Angle

5 类 15 个参数,每个参数映射到 1–3 个 BS 的权重范围。

参数 → BS 映射 ​

csharp
// 脸宽调整:同时影响 Face_Width_X 和 Cheek_Width
void ApplyFaceWidth(float value) {
    SetBlendShape("Face_Width_X", Mathf.Lerp(-50f, 50f, value));
    SetBlendShape("Cheek_Width", Mathf.Lerp(-20f, 30f, value * 0.6f));
}

注意第二个 BS 带了 0.6 的衰减系数——主形变和协同形变不是等权重的,协同项过强会让脸显得「充气」。每个映射的范围和系数都是美术在编辑器里逐个调出来的。

预设系统 ​

  • 随机生成:在合理范围内随机参数组合,给用户一个「摇骰子」入口;
  • 模板预设:女性 / 男性 / 可爱 / 帅气等方向的整组参数预设;
  • 用户保存:当前参数组合序列化成 JSON 存档,换设备可恢复。

预设模板的价值被低估了:大部分用户不会从零捏,而是在一个顺眼的模板上微调,模板质量直接决定留存。

性能 ​

每个 BS 调整是 O(1) 的顶点位移计算(权重 × 预存的顶点偏移),15 个 BS 同时调整在移动端不超 2ms,滑块拖动全程实时预览无压力;PC 端可支持到 50+ BS,给更精细的形变留了余量。

指标 ​

指标数值
用户可调参数15 个(5 类)
单参数 BS 映射1–3 个,带范围与衰减系数
移动端 15 BS 同调< 2ms
PC 端 BS 上限50+

复盘 ​

  1. 参数化系统的核心是映射表数据化——写死在代码里的映射,美术每调一次都要发版,必死。
  2. 协同 BS 的衰减系数比主 BS 的范围更影响「自然感」,调参时间主要花在这。
  3. 性能不是捏脸的瓶颈(O(1) 位移很便宜),真正的瓶颈是美术做 BS 的产能,参数表设计要按产能倒推。
  4. 存档只存参数不存结果——存最终 Mesh 既大又会和 BS 版本脱节,参数 + BS 版本号才是可维护的存档格式。

罗子初 · AI 座舱专家 · 8年端侧研发

Lv.1