项目背景:一个不可能完成的任务

各位好,我是老周。在火星人教育带了十年 MAYA 课程,经手的角色没有一千也有八百,但去年接的这部院线动画电影《长发公主》的 CG 角色造型,差点让我把半辈子攒的招牌砸了。导演的要求就一句话:”我要她每一根头发丝都像刚做过 Spa 一样,有生命感。” 但项目周期只有 30 天,角色有三套造型,每套 20 万根引导线,还要做动态解算。当时团队里两个新人连 XGen 的 Description 和 Modifier 都分不清,我就知道,这活儿得用点非常规手段。

今天这篇文章,我就把这个项目从头到尾的完整复盘讲给你听。不是那种参数朗读机式的教程,而是把我们在需求分析、技术选型、踩坑、解决、交付五个阶段里,所有值得记住的经验教训,一次性倒给你。你会在里面看到我怎么用 XGen 的修饰器体系(Modifier Stack)搭出三层毛发结构,怎么用表达式(Expression)驱动发丝的动态细节,以及最后我们是怎么用分层渲染(Layer-based Rendering)把 20 万根头发驯服成电影级画面的。

第一阶段:需求分析——别急着动手,先搞清楚头发是”资产”还是”角色”

项目启动第一天,我就把团队按在会议室里,问了一个灵魂问题:”这头发,是资产还是角色?” 如果你觉得我在故弄玄虚,那你就错了。在 CG 流程里,这决定了后续所有技术路线的走向。

如果是”资产”,头发就是角色身上的一个可复用部件,重点在于可维护性和通用性,比如游戏里的换装系统。如果是”角色”,头发就是角色的灵魂延伸,它要有脾气、有情绪、有物理反馈,重点在于表现力。我们这部电影的女主角,她的长发不只是装饰,更是她魔法力量的延伸,所以我把她定义为”角色”。

基于这个定位,我们制定了三条硬性需求:

  • 视觉需求:发丝要根根分明,有高光、有次表面散射(SSS),动态时要有流动感,不能是僵硬的面片。
  • 技术需求:要支持角色表演时的大幅运动,包括甩头、跳跃、甚至倒立,解算必须稳定,不能穿帮。
  • 流程需求:三套造型(日常、战斗、魔法释放)要能快速切换,且渲染效率要满足 4K 分辨率下的每日交付量。

需求明确后,我们画了一张简单的流程图:建模 → 引导线(Guides)→ 修饰器 → 动力学解算 → 渲染。这张图后来成了我们整个项目的”宪法”,每次有人偏离航线,我就拿它出来说事。

第二阶段:技术选型——为什么是 XGen?

你可能要说,现在市面上有 Yeti、Houdini 的 Vellum,还有 Arnold 的 Standard Hair,为什么偏偏选了 XGen?我的回答是:因为我们要的是”可控性”和”可迭代性”。

XGen 的核心是描述(Description)修饰器(Modifier)。一个 Description 定义了毛发的基本属性(数量、长度、密度),而 Modifier 则是一个非破坏性的操作栈,你可以像 Photoshop 的图层一样,随时增删、调整每一个步骤。这种架构让我们可以在不破坏基础引导线的前提下,反复试验不同的造型方案。

对比一下其他方案:

方案 优势 劣势 适用场景
XGen 完全集成在 Maya 中,可编程性极强,修饰器栈灵活 学习曲线陡峭,解算需要配合 nHair 或第三方插件 电影级角色,需要精细控制
Yeti 节点式操作直观,渲染快 需要额外购买,更新慢 视效电影、广告
Houdini Vellum 解算强大,支持 GPU 需要跨软件流程,资产转换成本高 特效镜头,大量动态

我选 XGen 还有一个私心:在火星人教育,我们教的就是 XGen,学生学完就能直接上手项目。所以,这个项目也是我们教学成果的试金石。

第三阶段:踩坑实录——那些年我们掉过的坑

技术方案定了,我们就开干。结果第一周就差点崩溃。下面这几个坑,你大概率也会遇到。

坑 1:引导线数量与质量的博弈

一开始,我们按照教程,在头皮上均匀放置了 200 根引导线,然后用 XGen 自动生成 20 万根发丝。结果渲染出来,发丝根部乱成一团,尤其是头顶漩涡处,像鸡窝一样。

排查原因,发现是引导线的分布没有考虑头皮的曲率。在头皮曲率大的地方(比如头顶),引导线之间的距离被拉大,导致生成发丝的插值不均匀。解决办法是:在曲率大的区域手动加密引导线,并在 `Create Description` 时,把 `Density` 设为 0.8,让 XGen 根据曲率自动调整插值权重。

技巧:引导线的分布永远要跟着头皮的 UV 走,不要偷懒用均匀分布。可以用 `Evaluate` 工具实时查看密度热力图,哪里发红就补哪里。

坑 2:修饰器栈的顺序——先粗细后颜色,还是先颜色后粗细?

第二个坑是修饰器顺序搞反了。我们一开始把 `Clump`(束状)修饰器放在 `Frizz`(卷曲)之前,结果头发像泡面一样,毫无垂感。后来才意识到,XGen 的修饰器是顺序敏感的,每一个修饰器都是基于上一个的结果进行修改。

正确的顺序应该是:先做基础形状(`Cut` 修剪长度)→ 再添加 `Frizz` 制造自然卷曲 → 然后用 `Clump` 成束 → 最后加 `Density` 和 `Width` 控制粗细。这样头发既有自然的杂乱感,又有清晰的束感。

我把这个顺序画成了一张流程图,贴在工位上,后来每个新来的实习生我都会先让他们背这个顺序。

坑 3:动态解算的穿帮——为什么头发总是穿透肩膀?

第三周,我们开始做角色的动态表演。结果一跑动,头发就无情地穿过了肩膀。我们试了 nHair 的碰撞,但 20 万根头发的碰撞计算量太大,渲染一次要 40 分钟。后来我换了个思路:用 XGen 的 `Expression` 来写一个伪碰撞。

原理很简单:在发丝顶点上计算与肩部球体的距离,如果小于半径,就把顶点沿法线方向推出。这个表达式用 MEL 写,挂在修饰器栈里,每次解算时自动执行。虽然物理上不精确,但视觉上完全够用,而且计算量几乎为零。

这里分享一下核心代码片段:

// 伪碰撞表达式,用于发丝顶点与肩部球体
// 假设 shoulderSphere 是肩部球体
for (i = 0; i < numVerts; i++) {
    vector $pos = pointPosition(shape, i);
    vector $diff = $pos - $sphereCenter;
    float $dist = mag($diff);
    if ($dist < $radius) {
        vector $norm = $diff / $dist;
        $pos = $sphereCenter + $norm * $radius;
        setPointPosition(shape, i, $pos);
    }
}

注意:这个表达式只是权宜之计,如果要更真实的物理交互,还是得用 nHair 配合 `Collide` 属性,但那样性能会下降。我们最终在远景镜头用表达式,近景特写才上 nHair。

第四阶段:解决方案——三层毛发结构与分层渲染

踩完坑,我们总结出一套"三层毛发"的方法论,这是项目的核心突破。

三层结构

我们不再用单一 Description,而是拆成三个层:

  • 底层(Base Layer):密度最高,负责发量感,长度稍短,颜色最深,作为阴影基础。
  • 中层(Mid Layer):密度中等,长度稍长,颜色过渡,负责整体形态。
  • 顶层(Top Layer):密度低,长度最长,颜色最亮,负责高光和动态飘动感。

这样做的原因是:渲染时可以用不同层级的参数控制,底层加噪点,顶层加高光,层次感瞬间拉满。而且,如果某个层出了问题,可以单独修改,不用全部重做。

分层渲染

渲染上,我们用了 Arnold 的 `aiHair` 节点,并拆分了三个 AOV 通道:`diffuse`、`specular`、`sss`。这样在合成时,可以分别调整颜色、高光强度和皮肤透光效果,而不是在渲染时一味调参数。

特别是 SSS(次表面散射),头发的半透明感全靠它。我们在 Arnold 里把 `single_scatter` 和 `multi_scatter` 都开了,并调高了 `random walk` 的深度,让光线在发丝内部多次弹射,模拟出头发在逆光下的边缘透光效果。

最终渲染效率从最初的 40 分钟一帧,优化到了 12 分钟一帧,完全满足每日交付量。

第五阶段:交付——当导演说"就是这种感觉"

最后一周,我们把三套造型全部做出来,并渲染了一条 30 秒的测试镜头。导演看完后沉默了三秒,然后说:"就是这种感觉,尤其是发梢的卷曲和光影,太灵动了。" 那一刻,我知道我们成了。

回顾整个项目,我觉得最值得分享的不是哪个参数调得好,而是那个"需求分析"的起点。很多 CG 艺术家拿到项目就急着建模、刷权重,却忽略了"这头发是资产还是角色"这个根本问题。想清楚这一点,后面的技术选择才有的放矢。

最后,我想说,XGen 不是万能的,但它在角色造型上的可控性,至今没有哪个工具能完全取代。希望这篇复盘能让你少走一些弯路。如果你也在做类似的项目,欢迎在评论区和我交流。

(技术展示图:XGen 修饰器栈和表达式编辑界面)

(成果作品图:电影角色最终渲染图)

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。