图形学——Cell/Worley Noise

Cell Noise, 有的又叫 Worley Noise,基于 voronoi,由Steven Worley在1996年提出了新的算法,新的算法并不能用来对既定的特征点进行 voronoi 区域划分,因此更多的被用来生成噪声,因此又被称作Worley Noise

1、理论
voronoi的原始实现在一个博客里有相关细节,iq的文章则是采用了Steven Worley的算法,算法步骤在网站有讲到,下面就以iq最基础的示例源码来做分析(代码我做了精简和修改,但依然可以生成voronoi图)

// The MIT License
// Copyright © 2013 Inigo Quilez

vec2 hash( vec2 p ) { p=vec2(dot(p,vec2(127.1,311.7)),dot(p,vec2(269.5,183.3))); return fract(sin(p)*18.5453); }

// return distance, and cell id
vec2 voronoi( in vec2 x )
{
    vec2 n = floor( x );
    vec2 f = fract( x );

    vec3 m = vec3( 8.0 );
    for( int j=-1; j<=1; j++ )
    for( int i=-1; i<=1; i++ )
    {
        vec2  g = vec2( float(i), float(j) );
        vec2  o = hash( n + g );
        vec2  r = g - f + o;
        float d = dot( r, r );
        if( d<m.x )
            m = vec3( d, o );
    }

    return vec2( sqrt(m.x), m.y );
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 p = fragCoord.xy/max(iResolution.x,iResolution.y);

    // computer voronoi patterm
    vec2 c = voronoi( 4.0*p );

    // colorize
    vec3 col = vec3(c.y);   

    fragColor = vec4( col, 1.0 );
}

iq的算法是在GPU上跑的,因此voronoi的特征点并非是一开始就已经定下来的,而是在计算的过程中生成的,而生成的核心就是伪随机函数 hash,因为是伪随机函数,因此只要输入不变,输出的随机数就不会变。其大概流程如下:
1、将uv划分为4x4的网格,取整数得到网格顶点,取小数得到像素在网格中的位置
2、对于每一个像素,遍历其对应的网格以及相邻的8个网格
3、每次遍历,根据网格位置通过伪随机函数生成特征点,然后计算像素到特征点的距离,代码中写的是 r = g - f + o, 这是因为伪随机函数的结果总是处于 [0, 1) ,必须加上顶点位移才能正确的得到像素和特征点的距离,然后用 m 记录距离最小的距离值和对应的特征点
4、最后,通过对应的特征点是否一致就能判断像素是否属于同一多边形内,在图形上则更直观,只需要将像素对应的特征值与像素颜色直接挂钩就可以了

上面的代码在Unity中运行效果如下:


实际上在计算距离的时候,还可以使用其他的距离度量,下面是不同的距离度量下增加了着色的结果:



欧几里得距离

曼哈顿距离

切比雪夫距离

在作为Worley Noise使用的时候,我们常常将最近特征点距离作为灰度值输出


通常将像素到特征点的距离称为” F values”, 如 F1是到最近特征点距离, F2是到第二近的特征点距离,以此类推,下图即为 F2 的灰度值作为颜色输出:


以及 F2 - F1我们可以得到泰森多边形的边缘:


关于voronoi 边缘, iq在他的文章里对 F2-F1的方法进行了改进,可以得到更加准确的边缘,具体推导可参考iq原文
在 F2-F1的基础上,将灰度作为高度生成法线,计算光照和反射后的结果如图所示:


接下来我们扩展到三维,算法是一样的,只是在计算距离和生成特征点的时候多了一个维度,在 Unity 中实现如下:


想比如二维通过输入缩放后的UV坐标来得到结果,但是这样在闭合曲面的UV接缝处就难免会有明显的偏差,在三维中,我们可以通过输入像素的模型或者世界坐标的线性变换结果来得到最后的voronoi图,这样得到的结果在闭合曲面上就是连续的

我们还可以通过raycast 绘制三维的 F2-F1 等位面如图所示:


最后, iq还在另一文章提供了由voronoi图生成噪声的方法,并命名为 voronoise, 具体代码实现和展示在文章里都有,这里就不再赘述了

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Babylon.js提供了一些内置的工具来生成柏林噪声Worley噪声和分形噪声。下面是一些示例代码: 1. 生成柏林噪声: ```javascript // 创建一个柏林噪声纹理 var noiseTexture = new BABYLON.NoiseProceduralTexture("noise", 256, scene); // 设置柏林噪声的类型和参数 noiseTexture.animationSpeedFactor = 5; noiseTexture.persistence = 0.9; noiseTexture.brightness = 0.1; noiseTexture.octaves = 4; // 将柏林噪声纹理应用到一个平面上 var plane = BABYLON.Mesh.CreatePlane("plane", 10, scene); var mat = new BABYLON.StandardMaterial("mat", scene); mat.diffuseTexture = noiseTexture; plane.material = mat; ``` 2. 生成Worley噪声: ```javascript // 创建一个Worley噪声纹理 var noiseTexture = new BABYLON.WorleyNoiseProceduralTexture("noise", 256, scene); // 设置Worley噪声的类型和参数 noiseTexture.animationSpeedFactor = 5; noiseTexture.distanceFunction = BABYLON.WorleyNoiseProceduralTexture.EUCLIDIAN_DISTANCE; noiseTexture.cellsDensity = 2.0; // 将Worley噪声纹理应用到一个平面上 var plane = BABYLON.Mesh.CreatePlane("plane", 10, scene); var mat = new BABYLON.StandardMaterial("mat", scene); mat.diffuseTexture = noiseTexture; plane.material = mat; ``` 3. 生成分形噪声: ```javascript // 创建一个分形噪声纹理 var noiseTexture = new BABYLON.FractalNoiseProceduralTexture("noise", 256, scene); // 设置分形噪声的类型和参数 noiseTexture.animationSpeedFactor = 5; noiseTexture.type = BABYLON.FractalNoiseType.FBM; noiseTexture.persistence = 0.6; noiseTexture.octaves = 4; // 将分形噪声纹理应用到一个平面上 var plane = BABYLON.Mesh.CreatePlane("plane", 10, scene); var mat = new BABYLON.StandardMaterial("mat", scene); mat.diffuseTexture = noiseTexture; plane.material = mat; ``` 这些示例代码演示了如何在Babylon.js中生成柏林噪声Worley噪声和分形噪声,并将它们应用到一个平面上。你可以通过修改参数和设置不同的纹理类型来创建自己的噪声效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值