《The Book of Shader》笔记 : 第六节 - 噪声入门小结

写在前面

之前搜索云雾,火焰,水等效果的时候,噪声一词频繁出现,因而一直想搜索相关概念。现在终于到了这章节。这两节内容从随机数讲起,而后介绍了Value noise, Perlin noise,Simplex noise噪声纹理的生成。
(预告:这章之后,理解了噪声与分形的有关概念,大概可以理解这张简单的二维云彩效果是如何生成的了。)
在这里插入图片描述

读完书中的内容,觉得理解起来还差点意思,搜索了一下其他前人的总结。
原地转粉的女神谈谈图形学
书中常常出现的大神Inigo Quiles的博客:https://www.iquilezles.org/www/index.htm

看了前辈们的文章之后,我的认知再次被刷新,不禁再次感叹,图形大法博大精深。

正文

Perlin 噪声

二维情况下Perlin噪声为例。

  1. 首先每个网格顶点生成一个随机单位向量。
    在这里插入图片描述
    // -------- 随机函数 -------- //
vec2 random2(vec2 st) {
    st = vec2(dot(st, vec2(127.1, 311.7)), dot(st, vec2(269.5, 183.3)));
    return -1.0 + 2.0 * fract(sin(st) * 43758.5453123);
}

  1. 计算网格顶点到网格中一点(s, t)的距离向量。
    在这里插入图片描述
  2. 得到网格顶点处的值,做法是用网格顶点上的随机向量点乘对应的距离向量。
  3. 求出(s, t)处对应的值。采用插值的方法,这里常用的插值函数是
    在这里插入图片描述
    选取三次函数的原因,该函数一阶导的导函数,在顶底点处函数值为0。相邻网格做插值时可满足一阶导连续,变化不会显得太突兀。

// -------- 2 维 Perlin Noise函数 -------- //

vec2 random2(vec2 st) {
    st = vec2(dot(st, vec2(127.1, 311.7)), dot(st, vec2(269.5, 183.3)));
    return -1.0 + 2.0 * fract(sin(st) * 43758.5453123);
}
float perlin_noise(vec2 st) {
    vec2 i = floor(st);
    vec2 f = fract(st);
    vec2 u = f * f * (3.0 - 2.0 * f);
    return mix(
        mix(
            dot(random2(i + vec2(0.0)), f - vec2(0.0)),
            dot(random2(i + vec2(1.0,0.0)), f - vec2(1.0, 0.0)),
            u.x
        ),
        mix(            
            dot(random2(i + vec2(0.0, 1.0)), f - vec2(0.0, 1.0)),
            dot(random2(i + vec2(1.0,1.0)), f - vec2(1.0, 1.0)),
            u.x
        ),
        u.y
    );
}

Perlin Noise分型效果演示:
在这里插入图片描述

Value Noise

  1. 同样的也是先在每个网格顶点生成一个介于[0, 1]间的随机值(这回不必生成以一个单位随机向量)。
  2. 直接使用顶点上的四个值,对网格中的像素点进行插值。
  3. // -------- 2 维 Value Noise函数 -------- //
float random21(vec2 st) {
    st = vec2 (dot(st, vec2(127.1, 311.7)),dot(st, vec2(269.5,183.3)));
    return -1.0 + 2.0 * fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453123);
}

float value_noise(vec2 st) {
    vec2 i = floor(st);
    vec2 f = fract(st);

    vec2 u = f * f * (3.0 - 2.0 * f);
    // 先沿x方向进行一次插值,然后再沿y方向做一次插值
    return mix(
        mix(random2(i + vec2(0.0)), random2(i + vec2(1.0, 0.0)), u.x),
        mix(random2(i + vec2(0.0, 1.0)), random2(i + vec2(1.0, 1.0)), u.x),
        u.y 
    );
}

Value Noise分型效果演示:
在这里插入图片描述

Simplex Noise

Simplex 是对Perlin噪声的一种改进,该方法的计算复杂度为在这里插入图片描述
在二维空间中,Simplex Noise用三个顶点构成一个网格,然后利用网格上的顶点进行插值,表示空间中任一点的值。推广到N维空间中则由N + 1 个顶点构成网格。
在这里插入图片描述
而之前的 Value Noise 与Perlin Noise 的复杂度为

在这里插入图片描述
二维空间中需要利用四个顶点进行插值,n维空间中需要利用到2 的 n 次方个顶点。
在这里插入图片描述

随着输入向量维度的增加,Simplex Noise计算复杂度上的优势会越明显。

但该算法的问题在于相对较难理解。

vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
vec2 mod289(vec2 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
vec3 permute(vec3 x) { return mod289(((x*34.0)+1.0)*x); }

float snoise(vec2 v) {

    // Precompute values for skewed triangular grid
    const vec4 C = vec4(0.211324865405187,
                        // (3.0-sqrt(3.0))/6.0
                        0.366025403784439,
                        // 0.5*(sqrt(3.0)-1.0)
                        -0.577350269189626,
                        // -1.0 + 2.0 * C.x
                        0.024390243902439);
                        // 1.0 / 41.0

    // First corner (x0)
    vec2 i  = floor(v + dot(v, C.yy));
    vec2 x0 = v - i + dot(i, C.xx);

    // Other two corners (x1, x2)
    vec2 i1 = vec2(0.0);
    i1 = (x0.x > x0.y)? vec2(1.0, 0.0):vec2(0.0, 1.0);
    vec2 x1 = x0.xy + C.xx - i1;
    vec2 x2 = x0.xy + C.zz;

    // Do some permutations to avoid
    // truncation effects in permutation
    i = mod289(i);
    vec3 p = permute(
            permute( i.y + vec3(0.0, i1.y, 1.0))
                + i.x + vec3(0.0, i1.x, 1.0 ));

    vec3 m = max(0.5 - vec3(
                        dot(x0,x0),
                        dot(x1,x1),
                        dot(x2,x2)
                        ), 0.0);

    m = m*m ;
    m = m*m ;

    // Gradients:
    //  41 pts uniformly over a line, mapped onto a diamond
    //  The ring size 17*17 = 289 is close to a multiple
    //      of 41 (41*7 = 287)

    vec3 x = 2.0 * fract(p * C.www) - 1.0;
    vec3 h = abs(x) - 0.5;
    vec3 ox = floor(x + 0.5);
    vec3 a0 = x - ox;

    // Normalise gradients implicitly by scaling m
    // Approximation of: m *= inversesqrt(a0*a0 + h*h);
    m *= 1.79284291400159 - 0.85373472095314 * (a0*a0+h*h);

    // Compute final noise value at P
    vec3 g = vec3(0.0);
    g.x  = a0.x  * x0.x  + h.x  * x0.y;
    g.yz = a0.yz * vec2(x1.x,x2.x) + h.yz * vec2(x1.y,x2.y);
    return 130.0 * dot(m, g);
}

Simplex Noise 噪声分型效果。
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值