图形学 Cellular Noise

发布时间 2023-10-04 16:20:07作者: 爱莉希雅

前言

本篇重点如何实现Cellular Noise

定义

  • Cellular Noise基于Voronoi图生成,其外观就像是一个个紧挨着的细胞,因而得名Cellular Noise。而Voronoi图的定义是由一组连续多边形组成,多边形的形成由其内部的控制点来控制,按照最邻近原则划分平面,即每个多边形都代表平面上离其内部控制点最近的区域

用途

  • 可用于模拟地表、水面的焦散、卷积云、爬行动物的皮肤等

实现

从迭代开始

  • 在shader中迭代即是for循环,需要注意的是:迭代的次数必须是固定的,并不接受动态次数

  • 在上面说过Cellular Noise是基于距离实现的,而这个距离是指一个特征点集最近的点的距离
    image-20231004143318887

  • 假设现在有四个特征点,求它的距离场可能的实现如下

    void mainImage( out vec4 fragColor, in vec2 fragCoord )
    {
        // Normalized pixel coordinates (from 0 to 1)
        vec2 uv = fragCoord/iResolution.xy;
        // 进行拉伸处理
        uv.x *= iResolution.x / iResolution.y;
    
        // Time varying pixel color
        vec3 col = vec3(0.f);
        
        // cell position
        vec2 point[5];
        point[0] = vec2(0.83,0.75);
        point[1] = vec2(0.60,0.07);
        point[2] = vec2(0.28,0.64);
        point[3] = vec2(0.31,0.26);
        point[4] = iMouse.xy / iResolution.xy;
        
        float minDistance = 1.f;
        
        // get min distance
        for(int i = 0; i < 5; ++i)
        {
            float dist = distance(uv, point[i]);
            
            minDistance = min(dist, minDistance);
        }
        
        col += minDistance;
    
        // Output to screen
        fragColor = vec4(col,1.0);
    }
    

    image-20231004144614357

划分空间

  • 上面这种策略虽然行得通,但性能不高,并没有充分发挥GPU并行架构的特点。一个可行的方案是将整个空间划分为多个网格——对uv进行缩放

    现在我已uv坐标作为color进行输出,可以得到以下结果
    image-20231004145339217
    如果对uv放大3倍并对其运用fract()求得小数部分,会得到以下结果
    image-20231004145535272

    这就是所谓的划分空间:将原本一个空间,划分为九个,大大提高shader性能

  • 对于Cellular Noise,不能延用之前使用的方案。现在每个网格对应一个特征点,计算目标像素点到九个特征点的最近距离image-20231004152034852
    可能的实现如下

    vec2 random2 (vec2 p)
    {
        return fract(sin(vec2(dot(p,vec2(127.1,311.7)),dot(p,vec2(269.5,183.3))))*43758.5453);
    }
    
    void mainImage( out vec4 fragColor, in vec2 fragCoord )
    {
        // Normalized pixel coordinates (from 0 to 1)
        vec2 uv = fragCoord/iResolution.xy;
        // 进行拉伸处理
        uv.x *= iResolution.x / iResolution.y;
        uv *= 3.f;
        vec2 uvI = floor(uv);
        vec2 uvF = fract(uv);
    
        // Time varying pixel color
        vec3 col = vec3(0.f);
        
        float minDistance = 1.f;
        
        // 计算目标点到九个特征点的距离
        for(int i = -1; i <= 1; ++i)
        {
            for(int j = -1; j <= 1; ++j)
            {
                // 目标特征点
                vec2 neighbor = vec2(float(i), float(j));
                vec2 point = random2(uvI + neighbor);
                
                // calc min distance
                float dist = length(point - uvF + neighbor);
                minDistance = min(minDistance, dist);
                
            }
        }
        
        col += minDistance;
        
        // Draw cell center
        col += 1.-step(.02, minDistance);
    
        // Output to screen
        fragColor = vec4(col,1.0);
    }
    

    image-20231004153329329

reference

https://zhuanlan.zhihu.com/p/94632440

https://iquilezles.org/articles/smoothvoronoi/

https://thebookofshaders.com/12/

https://en.wikipedia.org/wiki/Cellular_noise

https://thebookofshaders.com/09/?lan=ch

https://juejin.cn/post/7057916320431472670