Worley Noise

Worley Noise1(也称为 Voronoi noise 和 Cellular noise)是由 Steven Worley2 在 1996 年引入的一种噪声函数。

worley noise

它的样子看起来就是生物细胞的样子,由一个个 cell 构成,作者本身也是叫它“Cellular Texture”。

Worley Noise 的生成算法是:将整个图像分为一个个小方格,每个小方格中会有1个特征点,每个像素点的数值,是它到周围单元格中特征点的距离的最小值。

由于每个小方格只有1个特征点,每个像素只需要检查临近的9个小方格,一共只需要对比9个点来计算出它的值。

这里有2个在游戏中的应用案例3,分别是龟裂的干旱地面和造型夸张的烟柱。

cracked desert floor

billowy smoke pillar

Shadertoy

在本次代码实现过程中,我主要学习自Suboptimal Engineer4和The Book of Shaders5,并在Shadertoy6上进行了具体的代码编写。

同时,在iquilezles7这个网站有很多图形学相关的知识可以进行参考。

Grid Code

进入 Shadertoy,输入下列的代码:

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec3 col = vec3(0.0);

    fragColor = vec4(col,1.0);
}

此时显示纯黑色。

code black

将坐标变为正方形方格坐标,每个方格的范围在$[-0.5, +0.5]$之间。

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = fragCoord/iResolution.y;
    
    uv = uv * 4.0;
    
    vec2 gird = floor(uv);
    vec2 coord = fract(uv) - 0.5;

    vec3 col = vec3(coord, 0);

    fragColor = vec4(col,1.0);
}

code color coord

根据距离每个方格边框的距离,来进行上色。

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = fragCoord/iResolution.y;
    
    uv = uv * 4.0;
    
    vec2 gird = floor(uv);
    vec2 coord = fract(uv) - 0.5;

    float distanceGrid = 2.0 * max(abs(coord.x), abs(coord.y));

    vec3 col = vec3(distanceGrid);

    fragColor = vec4(col,1.0);
}

code distance grid

只在临近边界的地方进行上色,并添加颜色为红色。

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = fragCoord/iResolution.y;
    
    uv = uv * 4.0;
    
    vec2 gird = floor(uv);
    vec2 coord = fract(uv) - 0.5;

    float distanceGrid = smoothstep(0.9, 1.0, 2.0 * max(abs(coord.x), abs(coord.y)));

    vec3 girdColor = vec3(1.0, 0, 0) * vec3(distanceGrid);

    vec3 col = girdColor;

    fragColor = vec4(col,1.0);
}

code grid red color

Cell Code

接下来做两件事:

  • 根据伪随机函数,确定每个方格的点的位置
  • 计算当前像素到临近9个方格点的距离,取最小值

先绘制所有方格点,每个方格中只有1个点。

vec2 random2( vec2 p )
{
    // add 0.5 to avoid vec2(0, 0) return (0, 0)
    return fract(sin(vec2(dot(p + 0.5, vec2(213.1,322.2)),dot(p + 0.5, vec2(513.1,312.2))))*51312.1234);
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = fragCoord/iResolution.y;
    
    uv = uv * 4.0;
    
    vec2 gird = floor(uv);
    vec2 coord = fract(uv) - 0.5;
    
    // Gird Color
    float distanceGrid = smoothstep(0.9, 1.0, 2.0 * max(abs(coord.x), abs(coord.y)));
    vec3 girdColor = vec3(1.0, 0, 0) * vec3(distanceGrid);

    // Cell Color
    float distancePoint = 1.0;
    
    for (int i=-1; i<=1; i++)
    {
        for (int j=-1; j<=1; j++)
        {
            vec2 near = vec2(float(i), float(j));
            
            vec2 point = near + 0.5 * sin(iTime + 6.2831 * random2(gird + near));
            
            float currentDistance = length(coord - point);
            
            distancePoint = min(currentDistance, distancePoint);
        }
    }
    
    vec3 pointColor = vec3(smoothstep(0.90, 1.0, 1.0 - distancePoint));

    vec3 col = girdColor + pointColor;

    fragColor = vec4(col,1.0);
}

在有了距离后,只要根据距离显示颜色就可以了。

vec2 random2( vec2 p )
{
    // add 0.5 to avoid vec2(0, 0) return (0, 0)
    return fract(sin(vec2(dot(p + 0.5, vec2(213.1,322.2)),dot(p + 0.5, vec2(513.1,312.2))))*51312.1234);
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = fragCoord/iResolution.y;
    
    uv = uv * 4.0;
    
    vec2 gird = floor(uv);
    vec2 coord = fract(uv) - 0.5;
    
    // Gird Color
    float distanceGrid = smoothstep(0.95, 1.0, 2.0 * max(abs(coord.x), abs(coord.y)));
    vec3 girdColor = vec3(1.0, 0, 0) * vec3(distanceGrid);

    // Cell Color
    float distancePoint = 1.0;
    
    for (int i=-1; i<=1; i++)
    {
        for (int j=-1; j<=1; j++)
        {
            vec2 near = vec2(float(i), float(j));
            
            vec2 point = near + 0.5 * sin(iTime + 6.2831 * random2(gird + near));
            
            float currentDistance = length(coord - point);
            
            distancePoint = min(currentDistance, distancePoint);
        }
    }
    
    vec3 pointColor = vec3(smoothstep(0.95, 1.0, 1.0 - distancePoint));
    
    vec3 distanceColor = vec3(smoothstep(0.2, 2.0, 1.7 - distancePoint));

    vec3 col = girdColor + pointColor + distanceColor;

    fragColor = vec4(col,1.0);
}

最后,如果移除辅助网格和中点,得到 Worley Noise 的图像。

code-final-worley-noise

Palettes

最后通过渐变插值来上一点颜色8

vec3 palette( in float t)
{
    vec3 a = vec3(0.5, 0.5, 0.5);
    vec3 b = vec3(0.5, 0.5, 0.5);
    vec3 c = vec3(1.0, 1.0, 1.0);
    vec3 d = vec3(0.0, 0.1, 0.2);
    return a + b * cos( 6.283185 * ( c * t + d ) );
}

vec2 random2( vec2 p )
{
    // add 0.5 to avoid vec2(0, 0) return (0, 0)
    return fract(sin(vec2(dot(p + 0.5, vec2(213.1,322.2)),dot(p + 0.5, vec2(513.1,312.2))))*51312.1234);
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = fragCoord/iResolution.y;
    
    uv = uv * 4.0;
    
    vec2 gird = floor(uv);
    vec2 coord = fract(uv) - 0.5;
    
    // Gird Color
    float distanceGrid = smoothstep(0.95, 1.0, 2.0 * max(abs(coord.x), abs(coord.y)));
    vec3 girdColor = vec3(1.0, 0, 0) * vec3(distanceGrid);

    // Cell Color
    float distancePoint = 1.0;
    
    for (int i=-1; i<=1; i++)
    {
        for (int j=-1; j<=1; j++)
        {

            vec2 near = vec2(float(i), float(j));
            
            vec2 point = near + 0.5 * sin(iTime + 6.2831 * random2(gird + near));
            
            float currentDistance = length(coord - point);
            
            distancePoint = min(currentDistance, distancePoint);
        }
    }
    
    vec3 pointColor = vec3(smoothstep(0.95, 1.0, 1.0 - distancePoint));
    
    vec3 distanceColor = vec3(palette(smoothstep(0.2, 2.0, 1.7 - distancePoint)));

    vec3 col = distanceColor;

    col += girdColor + pointColor;

    fragColor = vec4(col,1.0);
}

code final worley noise color palette


  1. wikipedia, Worley noise, https://en.wikipedia.org/wiki/Worley_noise ↩︎

  2. Steven Worley, (1996), A Cellular Texture Basis Function, https://cedric.cnam.fr/~cubaud/PROCEDURAL/worley.pdf ↩︎

  3. Por Ryan Brucks, (2016) , Getting the Most Out of Noise in UE4, https://www.unrealengine.com/es-ES/tech-blog/getting-the-most-out-of-noise-in-ue4 ↩︎

  4. Suboptimal Engineer, (2023), What is Voronoi Noise? , https://www.youtube.com/watch?v=vcfIJ5Uu6Qw&ab_channel=SuboptimalEngineer ↩︎

  5. Patricio Gonzalez Vivo and Jen Lowe, The Book of Shaders, https://thebookofshaders.com/12/ ↩︎

  6. Shadertoy, https://www.shadertoy.com/ ↩︎

  7. iquilezles, float small and random, https://iquilezles.org/articles/sfrand/ ↩︎

  8. iquilezles, palettes - 1999, https://iquilezles.org/articles/palettes/ ↩︎