3. 噪波与分形:理解不同噪波类型、分形布朗运动、噪波叠加与混合

噪波,说白了就是程序化纹理的「地基」。我做了这么多年Houdini,发现很多新手一上来就想着调各种炫酷节点,结果做出来的东西要么太规则像塑料,要么乱成一团。其实问题往往出在——你没搞懂噪波是怎么工作的。

这一章,咱们就把它彻底讲透。

3.1 三种核心噪波:Perlin、Worley、Cellular

Houdini里内置了十几种噪波,但真正常用的,其实就三种。我个人习惯把它们分成「平滑派」、「细胞派」和「混合派」。

3.1.1 Perlin噪波——平滑自然的「万金油」

Perlin噪波是Ken Perlin在1983年发明的,用来生成自然纹理。它的特点是:连续、平滑、各向同性。说白了,就是看起来像云、像大理石、像地形起伏的那种感觉。

在VEX里调用很简单:

// 基本Perlin噪波
float n = noise(@P);
// 带频率和振幅控制
float n = noise(@P * 2.0) * 0.5;

我在项目中遇到过一个问题:用Perlin噪波做地形时,如果频率设得太高,会出现「锯齿感」。后来发现是因为采样点不够密。嗯,这里要注意——Perlin噪波在低频时表现最好,高频时建议用其他噪波替代

3.1.2 Worley噪波——细胞纹理的「硬核担当」

Worley噪波也叫Cellular噪波,它模拟的是细胞、岩石、马赛克那种「块状」结构。它的核心思想是:计算空间点到最近特征点的距离。

你想想看,为什么叫「细胞」?因为每个点都离自己的「细胞核」最近,自然就形成了多边形边界。

// Worley噪波
float n = noise(@P, "worley");
// 获取距离值(F1表示最近距离,F2表示次近距离)
float f1, f2;
f1 = xyzdist(1, @P, f2);

我曾经用Worley噪波做岩石裂缝纹理,效果出奇的好。但有个坑——Worley噪波默认输出的是距离值,范围是0到1,但分布不均匀。如果你直接用它做高度图,边缘会特别锐利。我的建议是:先做一次smoothstep再使用。

3.1.3 Cellular噪波——Perlin和Worley的「混血儿」

Cellular噪波其实不是独立算法,它是Perlin和Worley的混合体。Houdini里有个专门的cellnoise函数,它生成的是「每个晶胞内恒定值」的效果。

// Cellular噪波(每个格子内恒定值)
float n = cellnoise(@P);
// 配合随机旋转可以做出六边形蜂窝效果
vector pos = @P;
float n = cellnoise(pos * 2.0);

为什么需要它?因为Perlin太平滑,Worley太锐利,Cellular正好在中间——它保留了块状结构,但每个块内部是平滑过渡的。做瓷砖、地板、鳞片纹理时特别好用。

3.2 分形布朗运动(fBm)——从单层到多层

单层噪波有个致命问题:细节不够。你想想看,自然界里的石头、木头、云朵,哪个是单一频率的?都是大结构套小结构,层层嵌套。

fBm(分形布朗运动)就是解决这个问题的。它的原理很简单:把不同频率、不同振幅的噪波叠加起来

// 手动实现fBm
float fbm(vector pos; int octaves) {
    float value = 0.0;
    float amplitude = 0.5;
    float frequency = 1.0;
    for (int i = 0; i < octaves; i++) {
        value += amplitude * noise(pos * frequency);
        amplitude *= 0.5;
        frequency *= 2.0;
    }
    return value;
}

这里有个关键参数叫「持久度」(Persistence),也就是每次迭代振幅衰减的速度。持久度越高,高频细节越明显;持久度越低,整体越平滑。

核心公式:

fBm(P) = Σ (amplitude_i × noise(frequency_i × P))

其中 amplitude_i = persistence^i,frequency_i = lacunarity^i

我记得有一次做沙漠地形,用了6层fBm,结果渲染出来全是高频噪点。后来发现是持久度设成了0.7,太高了。对于地形,我建议持久度在0.4-0.5之间,否则会失去宏观结构

3.3 噪波叠加与混合——创造无限可能

单种噪波就像单一乐器,听着单调。叠加混合就像交响乐,能做出千变万化的效果。

3.3.1 加法混合

最简单的方式:把不同噪波的值加起来。

// Perlin + Worley 混合
float perlin = noise(@P);
float worley = noise(@P, "worley");
float mixed = perlin * 0.6 + worley * 0.4;

这种混合适合做「既有平滑起伏又有细节纹理」的效果,比如风化岩石。

3.3.2 乘法混合

乘法混合会产生「遮罩」效果——一个噪波控制另一个噪波的可见区域。

// 用Perlin遮罩Worley
float mask = noise(@P * 0.5);  // 低频遮罩
float detail = noise(@P * 4.0, "worley");
float result = mask * detail;

我在做苔藓纹理时就用过这招:用Perlin噪波控制苔藓的分布区域,用Worley噪波控制苔藓内部的细节。效果非常自然。

3.3.3 域扭曲(Domain Warping)

这是我最喜欢的技术。原理很简单:用第一个噪波的结果去偏移第二个噪波的采样位置

// 域扭曲
vector offset = vector(noise(@P), noise(@P + 100), noise(@P + 200));
vector warpedPos = @P + offset * 0.3;
float n = noise(warpedPos);

为什么这么做?因为直接叠加只是「值」的混合,而域扭曲是「结构」的混合。做出来的纹理会有流动感、扭曲感,像水波纹、烟雾、大理石纹路。

我的经验:

域扭曲的偏移量不要超过0.5,否则结构会完全破碎。我一般控制在0.1-0.3之间,既能产生扭曲效果,又保留原始结构。

3.4 知识体系结构图

下面这张图总结了本章的核心逻辑,我建议你保存下来,做纹理时对照着看:

噪波与分形知识体系 三大核心噪波 Perlin噪波 平滑自然,低频表现好 Worley噪波 细胞结构,边缘锐利 Cellular噪波 块状恒定,过渡平滑 分形布朗运动(fBm) 核心参数 Octaves:迭代层数 Persistence:持久度 Lacunarity:频率倍率 振幅衰减 = 持久度^i 叠加与混合 加法混合 值相加,保留双方特征 乘法混合 遮罩效果,控制可见区域 域扭曲 偏移采样位置,产生流动感 应用场景 地形生成 纹理贴图 特效模拟 材质混合 核心原则:单层噪波做结构,fBm做细节,混合做变化

3.5 避坑指南与实用技巧

最后,分享几个我踩过的坑,希望能帮你少走弯路:

我曾经犯过的错误:

  • 噪波范围没归一化:不同噪波的值域不同,Perlin在[-1,1],Worley在[0,1]。混合前一定要先归一化,否则结果会偏暗或偏亮。
  • fBm层数太多:超过6层后,肉眼基本看不出区别,但计算量翻倍。我一般控制在3-5层。
  • 域扭曲过度:偏移量太大时,结构会完全破碎。记住:域扭曲是「调味料」,不是「主菜」

我的调试技巧:

在Houdini里,我习惯先用visualize节点看噪波的灰度分布。如果大部分像素集中在0.3-0.7之间,说明对比度不够,需要做contrast调整。如果出现大量纯黑或纯白,说明范围溢出,需要fit到0-1。

好了,这一章的内容就到这里。噪波这东西,说难不难,说简单也不简单。关键是多动手试,多观察自然界里的纹理结构。你做得多了,自然就能「看山不是山,看水不是水」——看到任何纹理,脑子里都能拆解出它用了哪几种噪波、怎么混合的。


专注资料整理