第 13 章练习:常见 Noise 的选择
对应教程:13 常见 Noise 的结构与选择
起始示例:三类 noise 对比,修改 13-noise-families.js 和需要时的 13-noise-families.html。
练习 1:为效果选算法
**目标:**给下列效果各选一个优先尝试的算法,并写出关键理由:柔和云状遮罩、表面细胞斑点、石纹之间的细裂缝、需要可周期平铺的流动图案。
**要求:**最后一种效果不只回答算法家族,还要列出至少两个需要核对的实现能力。
**完成标准:**能区分 value / 梯度 / cellular 的结构,以及“算法名称”和“具体实现的周期能力”。
练习 2:显示梯度噪声的正负值
**目标:**在示例中新增“原始梯度值”模式:负值显示蓝色,零附近显示灰色,正值显示橙色。
**要求:**直接使用 gradientNoise(p) 的原始结果,不经过 0.5 + 1.1 * raw 的显示映射;其他 noise 模式维持现状。
**完成标准:**格点附近显示接近中间色,正负区域可分辨;改变阈值滑块不会影响这一模式。
练习 3:用 F2 - F1 找细胞边界
**目标:**在 cellular 模式中同时求最近距离 F1 和第二近距离 F2,用 F2 - F1 画出细胞边界。
**要求:**为严格包含第二近点,把搜索范围扩展到 -3~3 的单元;可新增一个显示模式。不要仅按当前单元的点距离判断。
**完成标准:**边界呈细线或窄带,特征点附近不会出现同样的亮线;能说明相较原来的 5 × 5 搜索,7 × 7 搜索增加了多少候选点,以及性能代价。
<details><summary>提示</summary>
保持两个最小值:新距离小于 F1 时,旧 F1 变成 F2;否则若新距离小于 F2,只更新 F2。
</details>
<a id=“reference-answers”></a>
第 13 章练习参考解答
对应练习:常见 Noise 的选择。
练习 1:为效果选算法
| 效果 | 优先尝试 | 理由 |
|---|---|---|
| 柔和云状遮罩 | 梯度噪声或 value noise | 两者都是连续场;随后用第 14 章的多尺度叠加增加细节 |
| 表面细胞斑点 | Cellular F1 |
到特征点的距离直接产生斑点中心 |
| 石纹细裂缝 | Cellular 的 F2 - F1 |
两个最近点等距的位置接近细胞边界 |
| 周期平铺的流动图案 | 明确支持周期的梯度 / simplex 实现 | 需核对周期参数与输入单位,还需核对时间变化是否在拼接处连续 |
如果资源有限,预生成可平铺的纹理也可以。名称叫“simplex”并不自动具有周期能力;动画还要确认时间维度与循环方式。
练习 2:显示梯度噪声的正负值
在 HTML 新增显示选项 value="2",例如“梯度原始正负值”。在片元 shader 的 main() 中,把 float level; 之前增加:
if ( uMode == 1 && uDisplay == 2 ) {
float raw = gradientNoise( p );
float magnitude = clamp( abs( raw ) * 2.2, 0.0, 1.0 );
vec3 middle = vec3( 0.35 );
vec3 target = raw < 0.0 ? vec3( 0.05, 0.28, 0.85 )
: vec3( 0.95, 0.35, 0.06 );
vec3 color = mix( middle, target, magnitude );
gl_FragColor = vec4( color, 1.0 );
#include <colorspace_fragment>
return;
}
这段代码直接使用有符号 raw 判正负。放在已有映射与阈值逻辑之前,因而阈值控件不会影响它。2.2 只控制可视化对比度,并不改变 noise 函数的输出。
练习 3:用 F2 - F1 找细胞边界
在片元 shader 中新增函数,使用示例已有的 featurePoint:
vec2 cellularF1F2( vec2 p ) {
vec2 cell = floor( p );
vec2 local = fract( p );
float first = 100.0;
float second = 100.0;
for ( int y = -3; y <= 3; y++ ) {
for ( int x = -3; x <= 3; x++ ) {
vec2 neighbor = vec2( float( x ), float( y ) );
vec2 site = neighbor + featurePoint( cell + neighbor );
float distanceToSite = length( site - local );
if ( distanceToSite < first ) {
second = first;
first = distanceToSite;
} else if ( distanceToSite < second ) {
second = distanceToSite;
}
}
}
return vec2( first, second );
}
在 HTML 新增显示选项 value="3",在 main() 中计算新模式的颜色:
if ( uMode == 2 && uDisplay == 3 ) {
vec2 distances = cellularF1F2( p );
float boundary = 1.0 - smoothstep( 0.02, 0.09,
distances.y - distances.x );
vec3 color = mix( vec3( 0.025, 0.055, 0.10 ),
vec3( 0.94, 0.58, 0.12 ), boundary );
gl_FragColor = vec4( color, 1.0 );
#include <colorspace_fragment>
return;
}
把这段放在原有 float level; 之前即可。两点等距时 F2 - F1 ≈ 0,边界变亮;特征点附近最近距离小,第二近距离通常较大,边界不亮。原来 5 × 5 = 25 个候选点,现在 7 × 7 = 49 个,hash 与距离计算接近翻倍;若叠加多层 cellular,应实测移动设备性能。