Skip to content

Houdini 20 Splash Screen

Splashscreen rendered in Karma XPU.

image-20260325122422284

untitled0001

请在此处下载 HIP 文件,然后一步一步拆分学习。

本项目没有官方视频解说,非常适合自己做探究学习,就比如这个文章。

这个作者很随性,我对项目部分代码做了简化和注释工作便于学习理解,还对部分做法做了更简单的替代示例。

IMPORTANT

本文章默认你已经大致了解 Groom 毛发和 Feather 系统,可以将本文看作是对官方文档的补充。

羽毛大致结构

了解这些部位对你来说就足够了。

image-20260325123708957

羽毛制作流程

  1. 羽毛参数。

    用到了超高的 Barb Density

    如果 Barb 密度比较低,那么你的羽毛会很有像素感。

    image-20260316143045836
  2. 对羽毛使用 Uncondense,输出 barblbarbr 和 Group shaft

    Uncondense 会将羽毛 Barb 转换成 Point,能够做出更多精细的调节,对比如图所示。

    TIP

    Condensed 羽毛是把 Barbs 的位置信息存储在 P_barb Attribute 中的,Uncondense SOP 做的仅仅是把位置数据从中提取出来并应用到真正的 Point 上。

    如果你不 Uncondense,你的很多逻辑只会去遍历 Shaft Points,如图一所示。

    image-20260325122643733image-20260325122653026

  3. 使用 Measure SOP 测周长 perimeter Attribute。

    后面会对羽毛做各种处理,比如添加噪点等。

    这些处理会导致毛长产生变化,因此需要记录处理前的长度便于后续将其恢复至原长度。

    NOTE

    对最终效果影响不大,可以忽略

    image-20260325123018343
  4. 使用 Enumerate SOP 为每个 Point 添加 index 、使用 Enumerate SOP 为每个 Prim 添加 id

    因为接下来使用 Blast SOP 提取 Vane 和 Shaft 的过程会扰乱原本的 Point Index,所以我们要在这之前记录其原来的 index ,便于在合并以后提取其 Point 位置并赋予到原羽毛上。

    为 Prim 添加 id 是因为接下来即将使用的 HairClump SOP 会隐性用到它。应该是新版本才有这个要求,因为原项目并没有 id

    你可以随意使用 HairGeneration SOP 生成一组毛发,然后通过移除 id 的方法测试这个问题。

    注:图中只有一个 Enumerate SOP,实际上应该是两个。

    image-20260326193529692
  5. 创建 GroomGuide 的前置 Attribute。

    使用 GuideTangentSpace SOP,左Guide连Vanes 、右Skin连 shaft

    GuideTangentSpace SOP 用来沿着 Curve (Guide) 在 Skin 上生成 TBN 坐标系的 T 和 N,如图二所示。

    这个 SOP 同时也会创建 skinprim Attribute。

    WARNING

    如果你在后续的 GuideMask SOP 处报错 skinprim,请不要忘了这一步。

    Normal Mode 需要被设为 SkinUVGradient

    image-20260316144013558

    以下是 T 和 N 的输出,N 朝左边:

    image-20260316144659652
  6. 使用 GuideMaskbarbl 上按 Bound 选取蒙板。

    将这个区域设置为 clumpmask ,然后用在 HairClump SOP 里。

    Guide 是羽毛 Curve, Skin 是羽毛的 Shaft。

    Bound 大小可看图里灰色描线部分。

    CAUTION

    要使用 HairClump SOP ,则需要用 Enumerate SOP 生成 id Prim Attribute!

    这点已在前段提到。否则你的羽毛不会有任何变化。

    image-20260316145146526image-20260316145427142
  7. 用同样的方法对羽毛下部分做 Bend。

    image-20260316150438657
  8. 截取比上面 Bend 更大一点的面积,继续做 Frizz。

    image-20260316150609172
  9. 使用 SetLength 来抑制被 Frizz 拉长的长度,将毛长设为前面提到的 perimeter 周长。

    image-20260316150819352
  10. 下面是 barbr 的处理流程,和上述流程大致相同。

    唯一区别是在最后一步多添了 Frizz。

    image-20260316151059435image-20260316151114089image-20260316151128507image-20260316151142190image-20260316151218919

  11. 将羽毛的位置映射到新羽毛上。

    由于 Blast SOP 的操作直接打乱了 Point Index,因此如果直接使用 Feather Match Uncondensed 会导致羽毛抽风。

    因此需要使用以下 Wrangle 把原羽毛的位置映射到新羽毛的位置:

    c
    int pt = findattribval(1, "point", "index", @ptnum);
    @P = point(1, "P", pt);

    当然如果你不喜欢用 Wrangle ,使用 Attribute Copy 也可以,记得勾选 Match Attribute 并将值设为 index ,需要确保新旧羽毛都有这个 Attribute。如图所示:

    image-20260326195404863
  12. 设置羽毛 UV。

    使用 UVProject 将羽毛的 UV 映射在 Atlas 贴图上,需要你先调好 UV 的大小。

    我喜欢使用 Ray 的方法做[羽毛 UV](羽毛 UV.md),更直观。

    image-20260316152438966
  13. 调整 Barb 粗细。

    使用 Wrangle 计算,利用 BBOX 计算单位化的曲线长度,让根部为0、顶部为1,再用 Ramp 重新映射。

    粗细程度可参考右上角曲线。

    image-20260316164954954
  14. 使用 Name SOP 为羽毛命名。

  15. 使用 Merge SOP 将不同羽毛合并在一起。

  16. 使用 Wrangle SOP 倍乘羽毛宽度,让羽毛宽度看着比较合理。

    如图所示,左图是改过的,右图是没改过的。

    NOTE

    Barb Density 越大、Width 应该越小,要不然就太粗了。

    image-20260316200921454image-20260316200935522

羽毛布置流程

image-20260316201329984
  1. 随意绘制两条蚊香曲线、用 Spiral SOP 创建一个相当标准的蚊香曲线。

    image-20260316205725772image-20260316205950073image-20260316210930500

  2. 使用 Convert 将 Polygon 曲线转换成 Nurbs 曲线,这样就会更平滑。然后再使用 Carve SOP 裁出来一小段曲线。

    曲线1和2都这样做,曲线2要比曲线1大一圈用作引导羽毛朝向。

    image-20260316211247548
  3. Resample SOP 重采两条样条线,注意 Segments 数量必须相同。然后使用 Wrangle 生成由小圈朝向大圈的法线。

    这里 resample_2 的 Segments 可以直接 Reference 到 resample_1 上,比如 ch("../resample_1/segs") ,这样可以更方便调整。

    image-20260316211645167
  4. 沿曲线设置 pscale ,调整羽毛大小。

    使用 vertexprimindex 遍历 Prim 上的顶点,将一整条曲线映射成 0~1 范围的 u 。然后把 Ramp 映射到 u 上。

    image-20260317104130424
  5. CopyToPoints 将标准的 line 拷贝到点上,然后使用 Resample SOP 细分线条,以此来制作羽毛的骨架。

    TIP

    羽毛种类和方向是由后面的 templatenamesbarborient Attribute 定义的。

    TIP

    可以添加 Bend 等效果为羽毛制作各种各样的变化。

    image-20260317110741699
  6. 基于先前制作的羽毛的 name 来构造 templatenames templateweights 用来定义羽毛类型。

    uniquevals VEX 可以返回所有 Prim 的 name 的值的数组,在这里表示羽毛种类的列表。

    IMPORTANT

    templatenamestemplateweights 必须是数组。

    NOTE

    templatenamestemplateweightsbarborient 是定义羽毛的必要 Attribute。

    稍后会使用 Feather Interpolate SOP 将羽毛呈现出来。

    image-20260317111026794
  7. 添加一个球体引导羽毛朝向,制作 barborient 羽毛朝向。

    使用 lookat 函数构建矩阵,逐点计算以下内容:

    • Tangent 前向量为 nextP - prevP (上一个点朝向下一个点的方向)。
    • Tangent 上向量为 upP - @p ( upP 是图中球心的位置, 即羽毛面向的方向)。
    • Tangent 侧向量由 Cross(Forward, Up) 计算。

    由此构建出 rot 矩阵,然后将其转换为四元数 barborient

    四元数旋转是由三个互相垂直的方向向量 TBN 组成的, lookat 函数就在帮你构建这个 TBN 坐标系。

    NOTE

    若当前遍历点为 i ,那么就用 i-1 表示上一个点、 i+1 表示下一个点,不要用 i 表示上一个点,这样可有效避免头尾点的位置差为 0 的情况,很聪明。

    image-20260317162124914

    如若不想使用 lookat 计算矩阵,可参考以下代码手动构建 TBN 矩阵然后转换四元数:

    image-20260317165635379
  8. Feather Interpolate SOP 创建羽毛。

    IMPORTANT

    注意导入 CdCd_barbrCd_barbl ,在 Condense 情况下 Cd 记录的是中心那条 Shaft 的颜色,并不会记录 Vanes 的颜色。

    image-20260317171632050

羽毛调整流程

  1. 添了基于 NoiseMask 的 Clump。

    话说这个不是应该在做毛的阶段去做吗,可能是这种情景可以更确切地细化?

    image-20260317175756692
  2. 给羽毛 Set Length,让羽毛不齐。

    image-20260322172327304
  3. 用 Ramp 调整羽毛 Width,让边缘有一种毛毛的感觉。

    image-20260317175934980
  4. 之后是 Spiral 羽毛,随机一下长度。

    image-20260317191232098
  5. 弯曲 150 度,然后加一些弯曲效果。

    项目使用 HairClump 里的 Curling 做的弯曲效果,但我测试下来使用 Guide Frizz 也可以实现类似的效果。Curl 效果类似于烟尘散开的样子。

    image-20260317191340390
  6. 做一次 Feather Clump,增加更多细节。

    image-20260317191526274
  7. 再做一次弯曲,看起来像布料一样有褶皱。第二张图是它的 Mask,使用 Attribute Noise SOP 制作。

    image-20260317191720107image-20260317191902645
  8. 使用 Feather Noise 轻微扰乱一下羽毛,左图是扰乱后。

    image-20260317192049323image-20260317192100175

  9. 设置随机长度,让羽毛变成绒毛。

    image-20260317192157350
  10. 设置羽毛宽度,消除空隙、让毛尖更柔和。

    宽度可参考右上角曲线。

image-20260317192312173
  1. 接下来是制作一些姿态特别曼妙的羽毛。

    这是最终样子。

    image-20260320135603941
  2. 随意绘制羽毛 Polygon 曲线。

    image-20260320135646883
  3. 转换成 Nurbs 曲线,然后 Resample 回 Polygon 曲线。

    Resample SOP 里要勾选 Tangent Attribute ,这会给每个 Point 都生成一个朝向下一 Point 方向的方向向量 tangentu

    image-20260320135735244
  4. 让羽毛不同部位朝着不同方向,使其姿态看起来很曼妙:

    代码大致解释,羽毛默认面向 (-1,0,0) 世界方向,然后制作一个绕着 tangentu 方向旋转 rotate * ramp 角度的 Quaternion,再将这个 Quaternion 应用到前面提到的默认方向 (-1,0,0) 上使其在每个 Point 上都不一样,得到一个方向向量 upD

    最后使用 lookat 函数控制最终 Quaternion 的法线方向 也就是羽毛面向。

    TIP

    作者的实现方法比较绕,图里是简化过的版本。

    我重新实现了个更加可控和更直观的版本,详见: 羽毛曼妙 文章。

    image-20260322151445115
  5. 将这些羽毛合并到一起,然后与之前做的羽毛合并起来,如图所示:

    image-20260322164014583image-20260322163856423

  6. 羽毛部分完成,轮到背景部分。

    1. 创建 HeightField,细分给高一点。
    2. 使用 HeightField Noise SOP,在 Distortion 里勾选 Enable Gradient WarpAccumulate Gradient Warp ,这两个勾选比较重点。
    3. 使用 Convert HeightField SOP 将 Grid Volume 转换为 Polygon。
    image-20260322173346884

LOP 渲染过程

先上节点图,如图所示,红圈部分是在构建一整个场景 USD,因为比较重点所以圈了出来。

参考文献:LOP 中关于 USD 的大纲 https://www.sidefx.com/docs/houdini/solaris/glossary.html

image-20260325102258189

0. 进入 SolarisLookDev 布局

Solaris 是使用 USD 描述场景的,因此你需要一个能够查看 PrimVar 和 USD Structure 的布局便于你进行调试。

将顶部布局从默认的 Build 切换为 SolarisLookDev 即可查看。

图中的 Scene Graph Path 就是你的 USD 结构,下面的空窗口能够查看 USD Prim 的各种属性,就像 JSON 一样。

Primitive Type 决定了这个 Prim 具有哪种功能,如相机 Camera、位移 XForm,或是单纯用来打组用的 Scope (它不支持位移,用来标记或当文件夹用),甚至是材质。

具体作用可参考 Houdini 准备的 USD 大纲。

image-20260325105415408

1. 导入羽毛

使用 SOP Import LOP 将羽毛模型导入,然后使用 Houdini Procedural: Feather LOP 初始化羽毛。

Houdini Procedural: Feather LOP 中,最小需要填 Procedural PrimGroom Rest 两项,分别定义了 羽毛创建的位置羽毛几何体 ,因此我们还需要在前面创建一个 Primitive LOP 或 XForm LOP 来定义创建位置:使用 Primitive LOP 在 /Asset/geo/groom_a 处创建一个默认预设的 XForm,或是用 XForm LOP 在那个位置创建并设置一个你喜欢的位置和旋转。

然后为你的 SOP Import LOP 定义一个导入路径 Import Path Prefix ,再把这个路径填入 Houdini Procedural: Feather LOP 中即可完成羽毛的导入。

TIP

Houdini Procedural: Feather LOP 中后面的 Deformer 两项是关于动画的,可参考官方文献 Feather Rendering ,本项目只做静态羽毛所以不需要填写。

NOTE

Primitive LOP 的主要用途是创建占位符、用来快速创建你的 USD 结构。

IMPORTANT

使用 / 开头的路径表示绝对路径,如果想使用相对路径可以去掉这个符号。

CAUTION

对于羽毛渲染,你稍后还需要使用 Preview Houdini Procedurals LOP 才能完整地把羽毛渲染出来,否则你只能看到羽毛 Curve。

Houdini Procedural: Feather LOP 会在这个 XForm 处生成一个新的副本,和你前面使用 SOP Import LOP 导入进来的东西没有半毛钱关系,可以删除加快渲染。如果需要把 Attribute 导入到羽毛上比如 Cd ,请在这个节点里填写!

image-20260325103353957

2.1 构建 USD

接下来是构建你的场景 USD。

在 TAB 中找到 Component Builder ,会自动为你创建以下节点,其中 INSERT_HERE 就是你 USD 的内容,右侧 Material Library LOP 是你的材质库, Component Material LOP 会定义哪个路径的模型用哪个材质。

Component Output LOP 中可以定义 USD 的名字。

WARNING

你极大可能会遇到材质抛出 Bound Set 相关的报错,这是软件的 BUG,你需要保证路径一定正确,然后重启软件就可以暂时解决这个问题。

这个问题目前没有被 SideFX 修复。

image-20260325111342687

2.2 在 USD 中导入羽毛和背景板

如图所示,使用 Merge LOP 把你的羽毛和背景板合并进来。

Render Geometry Settings LOP 是用来为上游 Prim 单独设置渲染模式的,项目中是把 Reflection LimitRefraction Limit 设置到了 10,并开启 Caustics 焦散效果。

image-20260325112112890

关于为什么需要为羽毛设置反射和焦散效果,这是因为 Houdini 的羽毛模型是像图一的构造:中心 Medulla 黑色素被 半透明 的 Cortex 皮质所包裹,而皮质又被 Cuticles 角质所包裹。

可以观看图二,你会清晰地发现有一层半透明的皮质,而这层皮质可以直接用玻璃材质来模拟。

可以参考文档 Karma HairKarma Fur ,后者是前者的扩展,增加了调整黑色素半径的功能 (半径为0的时候两者完全相同) 。

imgimg

2.3 制作羽毛材质

双击 Material Library LOP 进入,创建 Karma Material Builder 默认材质,再次双击进入。

创建 Karma Fur VOP 代替默认的 MtlX Standard Surface VOP,将 Dirt & Wetness 里的 Diffuse 设为 1,便于后续设置颜色。

然后是赋予颜色,该项目通过使用 Feather Ray SOP 直接把贴图纹理烘焙成了顶点色 Cd ,因此这里需要读取 Attribute:创建 MtlX Geomerty Property Value VOP,将 Geomprop 设置成 displayColor ,然后连接到羽毛颜色上,总体如图一所示。

CAUTION

在 USD 中,顶点色叫 displayColor 而非 Cd ,这里需要注意!

如果你不确定你的 Prim 都有哪些 Attributes,你可以通过左下角那如同 JSON 的属性表查看 primvars 开头的参数,如图二。

WARNING

如果你想往羽毛上导入 Attribute,你需要在最前面的 Houdini Feather: Procedural LOP 里设置,而不是在 SOP Import 里导入!

TIP

如果你没有烘焙顶点色而是使用 UV,你可以使用 MtlX Image VOP 来导入贴图。

image-20260325115419419image-20260325115712772

2.4 设置羽毛材质

制作好材质后,回到 Material Library LOP,我们需要将材质暴漏出来供外部使用。

设置好材质的 USD 路径 Material Path Prefix ,然后点击下方的 Auto-fill Materials 按钮。按钮点完以后会自动在下面列表里添加你的材质,注意修改 Material Path 匹配上面的 USD 路径,如图所示。

TIP

Assign To Geometry 是给上游几何体设置材质用的,我们这里并没有上游几何体,因此记得取消勾选。

稍后会在 Component Material LOP 中为咱们的模型分配材质。

IMPORTANT

别忘了查看左侧 USD 结构,确定材质路径没有毛病。

WARNING

真正起到渲染作用的是 mat_feather 里面你创建的 XForm ,直接把材质赋给 mat_feather 也会使其子类继承材质。

image-20260325120715713

暴漏完材质以后,在 Component Material LOP 中为羽毛几何体设置材质,如图所示,千万要对照 USD 结构去输入路径,也可以复制粘贴路径。

image-20260325121359315

CAUTION

你极大可能会遇到材质抛出 Bound Set 相关的报错,这是软件的 BUG,你需要保证路径一定正确,然后重启软件就可以暂时解决这个问题。

这个问题目前没有被 SideFX 修复。

最后,在 Component Output LOP 中为我们的 USD 赋予一个好听的名字,USD 构建结束。

如果显示出来的羽毛贴图是被拉长的,请先检查羽毛有没有 Cd_barbr Cd_barbl Attribute,然后在 Houdini Procedural Feather LOP 里的 Barb Attribute Sets 导入 Cd Attribute。

image-20260325121730024

3. 打光、设置渲染

本节没什么好所的,直接上图,图中红色划掉的节点对最终渲染没有影响。

image-20260325121833001

结束。

这张图片是我修改了材质,做了薄膜效果,很漂亮,节点参考下图。

untitled0001

image-20260325122132629

被你发现了,是假的效果... 当然,你可以在 MtlX Standard Surface VOP 中直接使用 Thin Film 做基于物理的薄膜效果,效果也很好看!

image-20260325122308348