第 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,应实测移动设备性能。