Houdini 20 Splash Screen



请在此处下载 HIP 文件,然后一步一步拆分学习。
本项目没有官方视频解说,非常适合自己做探究学习,就比如这个文章。
这个作者很随性,我对项目部分代码做了简化和注释工作便于学习理解,还对部分做法做了更简单的替代示例。
IMPORTANT
本文章默认你已经大致了解 Groom 毛发和 Feather 系统,可以将本文看作是对官方文档的补充。
羽毛大致结构
了解这些部位对你来说就足够了。

羽毛制作流程
羽毛参数。
用到了超高的
Barb Density。如果 Barb 密度比较低,那么你的羽毛会很有像素感。

对羽毛使用 Uncondense,输出
barbl和barbr和 Groupshaft。Uncondense 会将羽毛 Barb 转换成 Point,能够做出更多精细的调节,对比如图所示。
TIP
Condensed 羽毛是把 Barbs 的位置信息存储在
P_barbAttribute 中的,Uncondense SOP 做的仅仅是把位置数据从中提取出来并应用到真正的 Point 上。如果你不 Uncondense,你的很多逻辑只会去遍历 Shaft Points,如图一所示。


使用
MeasureSOP 测周长perimeterAttribute。后面会对羽毛做各种处理,比如添加噪点等。
这些处理会导致毛长产生变化,因此需要记录处理前的长度便于后续将其恢复至原长度。
NOTE
对最终效果影响不大,可以忽略

使用
EnumerateSOP 为每个 Point 添加index、使用EnumerateSOP 为每个 Prim 添加id。因为接下来使用
BlastSOP 提取 Vane 和 Shaft 的过程会扰乱原本的 Point Index,所以我们要在这之前记录其原来的index,便于在合并以后提取其 Point 位置并赋予到原羽毛上。为 Prim 添加
id是因为接下来即将使用的HairClumpSOP 会隐性用到它。应该是新版本才有这个要求,因为原项目并没有id。你可以随意使用
HairGenerationSOP 生成一组毛发,然后通过移除id的方法测试这个问题。注:图中只有一个
EnumerateSOP,实际上应该是两个。
创建 GroomGuide 的前置 Attribute。
使用
GuideTangentSpaceSOP,左Guide连Vanes 、右Skin连shaft。GuideTangentSpaceSOP 用来沿着 Curve (Guide) 在 Skin 上生成 TBN 坐标系的 T 和 N,如图二所示。这个 SOP 同时也会创建
skinprimAttribute。WARNING
如果你在后续的
GuideMaskSOP 处报错skinprim,请不要忘了这一步。Normal Mode需要被设为SkinUVGradient。
以下是 T 和 N 的输出,N 朝左边:

使用
GuideMask在barbl上按Bound选取蒙板。将这个区域设置为
clumpmask,然后用在HairClumpSOP 里。Guide是羽毛 Curve,Skin是羽毛的 Shaft。Bound大小可看图里灰色描线部分。CAUTION
要使用
HairClumpSOP ,则需要用EnumerateSOP 生成idPrim Attribute!这点已在前段提到。否则你的羽毛不会有任何变化。


用同样的方法对羽毛下部分做 Bend。

截取比上面 Bend 更大一点的面积,继续做 Frizz。

使用
SetLength来抑制被 Frizz 拉长的长度,将毛长设为前面提到的perimeter周长。
下面是
barbr的处理流程,和上述流程大致相同。唯一区别是在最后一步多添了 Frizz。





将羽毛的位置映射到新羽毛上。
由于
BlastSOP 的操作直接打乱了 Point Index,因此如果直接使用Feather Match Uncondensed会导致羽毛抽风。因此需要使用以下 Wrangle 把原羽毛的位置映射到新羽毛的位置:
cint pt = findattribval(1, "point", "index", @ptnum); @P = point(1, "P", pt);当然如果你不喜欢用 Wrangle ,使用
Attribute Copy也可以,记得勾选Match Attribute并将值设为index,需要确保新旧羽毛都有这个 Attribute。如图所示:
设置羽毛 UV。
使用
UVProject将羽毛的 UV 映射在 Atlas 贴图上,需要你先调好 UV 的大小。我喜欢使用 Ray 的方法做[羽毛 UV](羽毛 UV.md),更直观。

调整 Barb 粗细。
使用 Wrangle 计算,利用 BBOX 计算单位化的曲线长度,让根部为0、顶部为1,再用 Ramp 重新映射。
粗细程度可参考右上角曲线。

使用
NameSOP 为羽毛命名。使用
MergeSOP 将不同羽毛合并在一起。使用
WrangleSOP 倍乘羽毛宽度,让羽毛宽度看着比较合理。如图所示,左图是改过的,右图是没改过的。
NOTE
Barb Density越大、Width应该越小,要不然就太粗了。

羽毛布置流程

随意绘制两条蚊香曲线、用
SpiralSOP 创建一个相当标准的蚊香曲线。


使用
Convert将 Polygon 曲线转换成 Nurbs 曲线,这样就会更平滑。然后再使用CarveSOP 裁出来一小段曲线。曲线1和2都这样做,曲线2要比曲线1大一圈用作引导羽毛朝向。

用
ResampleSOP 重采两条样条线,注意 Segments 数量必须相同。然后使用 Wrangle 生成由小圈朝向大圈的法线。这里
resample_2的 Segments 可以直接 Reference 到resample_1上,比如ch("../resample_1/segs"),这样可以更方便调整。
沿曲线设置
pscale,调整羽毛大小。使用
vertexprimindex遍历 Prim 上的顶点,将一整条曲线映射成 0~1 范围的u。然后把 Ramp 映射到u上。
用
CopyToPoints将标准的 line 拷贝到点上,然后使用ResampleSOP 细分线条,以此来制作羽毛的骨架。TIP
羽毛种类和方向是由后面的
templatenames和barborientAttribute 定义的。TIP
可以添加 Bend 等效果为羽毛制作各种各样的变化。

基于先前制作的羽毛的
name来构造templatenamestemplateweights用来定义羽毛类型。uniquevalsVEX 可以返回所有 Prim 的name的值的数组,在这里表示羽毛种类的列表。IMPORTANT
templatenames和templateweights必须是数组。NOTE
templatenames和templateweights和barborient是定义羽毛的必要 Attribute。稍后会使用
Feather InterpolateSOP 将羽毛呈现出来。
添加一个球体引导羽毛朝向,制作
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 的情况,很聪明。
如若不想使用
lookat计算矩阵,可参考以下代码手动构建 TBN 矩阵然后转换四元数:
- Tangent 前向量为
用
Feather InterpolateSOP 创建羽毛。IMPORTANT
注意导入
Cd、Cd_barbr、Cd_barbl,在 Condense 情况下Cd记录的是中心那条 Shaft 的颜色,并不会记录 Vanes 的颜色。
羽毛调整流程
添了基于
NoiseMask的 Clump。话说这个不是应该在做毛的阶段去做吗,可能是这种情景可以更确切地细化?

给羽毛 Set Length,让羽毛不齐。

用 Ramp 调整羽毛 Width,让边缘有一种毛毛的感觉。

之后是 Spiral 羽毛,随机一下长度。

弯曲 150 度,然后加一些弯曲效果。
项目使用
HairClump里的Curling做的弯曲效果,但我测试下来使用Guide Frizz也可以实现类似的效果。Curl 效果类似于烟尘散开的样子。
做一次 Feather Clump,增加更多细节。

再做一次弯曲,看起来像布料一样有褶皱。第二张图是它的 Mask,使用
Attribute NoiseSOP 制作。

使用
Feather Noise轻微扰乱一下羽毛,左图是扰乱后。

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

设置羽毛宽度,消除空隙、让毛尖更柔和。
宽度可参考右上角曲线。

接下来是制作一些姿态特别曼妙的羽毛。
这是最终样子。

随意绘制羽毛 Polygon 曲线。

转换成 Nurbs 曲线,然后 Resample 回 Polygon 曲线。
Resample SOP 里要勾选
Tangent Attribute,这会给每个 Point 都生成一个朝向下一 Point 方向的方向向量tangentu。
让羽毛不同部位朝着不同方向,使其姿态看起来很曼妙:
代码大致解释,羽毛默认面向
(-1,0,0)世界方向,然后制作一个绕着tangentu方向旋转rotate * ramp角度的 Quaternion,再将这个 Quaternion 应用到前面提到的默认方向(-1,0,0)上使其在每个 Point 上都不一样,得到一个方向向量upD。最后使用
lookat函数控制最终 Quaternion 的法线方向 也就是羽毛面向。
将这些羽毛合并到一起,然后与之前做的羽毛合并起来,如图所示:


羽毛部分完成,轮到背景部分。
- 创建 HeightField,细分给高一点。
- 使用
HeightField NoiseSOP,在Distortion里勾选Enable Gradient Warp和Accumulate Gradient Warp,这两个勾选比较重点。 - 使用
Convert HeightFieldSOP 将 Grid Volume 转换为 Polygon。

LOP 渲染过程
先上节点图,如图所示,红圈部分是在构建一整个场景 USD,因为比较重点所以圈了出来。
参考文献:LOP 中关于 USD 的大纲 https://www.sidefx.com/docs/houdini/solaris/glossary.html

0. 进入 SolarisLookDev 布局
Solaris 是使用 USD 描述场景的,因此你需要一个能够查看 PrimVar 和 USD Structure 的布局便于你进行调试。
将顶部布局从默认的 Build 切换为 SolarisLookDev 即可查看。
图中的 Scene Graph Path 就是你的 USD 结构,下面的空窗口能够查看 USD Prim 的各种属性,就像 JSON 一样。
Primitive Type 决定了这个 Prim 具有哪种功能,如相机 Camera、位移 XForm,或是单纯用来打组用的 Scope (它不支持位移,用来标记或当文件夹用),甚至是材质。
具体作用可参考 Houdini 准备的 USD 大纲。

1. 导入羽毛
使用 SOP Import LOP 将羽毛模型导入,然后使用 Houdini Procedural: Feather LOP 初始化羽毛。
在 Houdini Procedural: Feather LOP 中,最小需要填 Procedural Prim 和 Groom 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 ,请在这个节点里填写!

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 修复。

2.2 在 USD 中导入羽毛和背景板
如图所示,使用 Merge LOP 把你的羽毛和背景板合并进来。
Render Geometry Settings LOP 是用来为上游 Prim 单独设置渲染模式的,项目中是把 Reflection Limit 和 Refraction Limit 设置到了 10,并开启 Caustics 焦散效果。

关于为什么需要为羽毛设置反射和焦散效果,这是因为 Houdini 的羽毛模型是像图一的构造:中心 Medulla 黑色素被 半透明 的 Cortex 皮质所包裹,而皮质又被 Cuticles 角质所包裹。
可以观看图二,你会清晰地发现有一层半透明的皮质,而这层皮质可以直接用玻璃材质来模拟。
可以参考文档 Karma Hair 和 Karma Fur ,后者是前者的扩展,增加了调整黑色素半径的功能 (半径为0的时候两者完全相同) 。


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 来导入贴图。


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 也会使其子类继承材质。

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

CAUTION
你极大可能会遇到材质抛出 Bound Set 相关的报错,这是软件的 BUG,你需要保证路径一定正确,然后重启软件就可以暂时解决这个问题。
这个问题目前没有被 SideFX 修复。
最后,在 Component Output LOP 中为我们的 USD 赋予一个好听的名字,USD 构建结束。
如果显示出来的羽毛贴图是被拉长的,请先检查羽毛有没有 Cd_barbr Cd_barbl Attribute,然后在 Houdini Procedural Feather LOP 里的 Barb Attribute Sets 导入 Cd Attribute。

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

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


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

