...
Back

把 3D 数字人送进浏览器,但别把整个工作室也送过去

一个带骨骼和真实动画的角色,是一份要经由你无法控制的网络抵达的大文件。大部分工程量花在「抵达」上,不是「渲染」上。

把 3D 数字人送进浏览器,但别把整个工作室也送过去

把 3D 数字人送进浏览器 🧍

在网页上放一个带骨骼、会动的角色,开头容易得有欺骗性,搞砸也容易得不同寻常。渲染器是有教程的那部分;而决定它对真实用户是否成立的,是从你的存储桶到第一帧之间的一切。


资产本身就是产品决策

一个角色模型带着骨骼、蒙皮网格、用于表情的 blend shape、材质和贴图。贴图占主导 —— 它占掉大半个文件是完全正常的,而一个从创作工具里直接导出的模型体积大到没有任何移动端访客愿意等,也是完全正常的。

在动渲染器之前,先决定这个模型必须做什么:

  • 一个待机时要显得活着的展示角色,需要完整的表情集和好材质。
  • 一个背景元素两者都不需要,把它们发出去是纯成本。
  • 一个要对口型、要有情绪的角色,需要保留并准确命名 blend shape —— 这会限制你允许优化掉的东西。

管用的顺序是:砍掉体验用不到的、压缩剩下的、最后再优化渲染循环。反过来做,等于在一个真正问题是贴图的模型上调 draw call。

具体说,可靠的收益来自:网格压缩、能在 GPU 上保持压缩而不是在显存里膨胀的贴图格式、把贴图缩到显示实际能分辨的尺寸、以及删掉没有任何东西引用的骨骼和 blend shape。每一项都是机械劳动。合起来,它们经常能把一个模型从"移动数据下不可用"带到"没问题"。


加载是一种用户体验,不是一个转圈图标

大文件走真实网络,意味着有一段可见的空窗,而大多数实现在这段空窗前放弃,摆一个转圈图标。

更好的做法:

永远不要让首屏渲染等模型。 角色加载期间,页面应该可读、可交互。如果角色就是页面本身,那就立刻渲染布局、文案和一张轻量的占位图,等模型就绪再换进来。

按感知重要性排序加载。 先几何体加一张低分辨率贴图,能很快得到一个认得出来的角色;高清那一遍可以随后到。一个先有点糊、然后变清晰的角色,读起来是"在加载"。一个凭空啪地出现的空白方块,读起来是"坏了"。

对那些本就不该收到它的访客,做明确的决定。 受限设备、省流量信号、减少动效偏好。占位图不是失败态;对某些访客来说它才是正确体验,而把它当成一等结果来对待,正是让页面对所有人都快的原因。

处理失败。 资源会超时。解码器会在老硬件上失败。必须存在一条"模型永远没来但页面依然完好"的路径,而且必须测试它 —— 因为真实存在一部分访客走的就是这条路。


动画的时间实际花在哪

把动画数据和角色分开是划算的做法 —— 一个模型、多段动作,每段都很小。实际困难很少是数学上的:

  • 命名是一份契约。 动画按名字驱动骨骼和 blend shape。一次改了名字的导出,产生的结果是沉默:没有报错、没有动作,除了比对两份名字列表之外无处可调。
  • 重定向是假设浮出水面的地方。 为一副骨骼创作的动作套到另一副上,会暴露比例和静止姿势的每一处差异,通常表现为肢体穿模。
  • 待机动作的价值高于它的成本。 一个会呼吸、会换重心的角色读起来是"在场"。一个纹丝不动的角色读起来是"渲染崩了",哪怕帧率是六十。

值得围绕它做设计的那条约束

3D 角色是页面上唯一一个胃口无上限的元素:更多面数、更大贴图、更多同时播放的动画 —— 每一项改进都唾手可得,而每一项都由访客在他们的设备上、用他们的网络来买单。

所以先把预算定下来,定成一个数字,在所有好玩的部分之前。一个总资源体积,一个目标设备。之后每一个决定 —— 这张贴图的尺寸、这套表情、这段额外动作 —— 都有了可对照的地方,而模型也就不会经由二十个各自都合理的提交,悄悄长成只有你的工作站能加载的东西。