图形学的数学基础(四十二):噪声-下

856 阅读2分钟

本文已参与[新人创作礼]活动,一起开启掘金创作之路

本文禁止转载,如需转载请注明出处。

图形学的数学基础(四十二):噪声-下

在上一章中,我们介绍并解释了实现一维噪声函数的大部分技术和方法。创建更高维度的噪声和一维噪声并没有本质上的区别,因为它们都是基于同样的方法和技术。之前提到过,所有的噪声函数都会返回一个浮点数,无论输入参数是浮点数,二维点还是三维点,至于一维噪声 二维或三维噪声仅与其输入值有关。二维噪声是将二维点作为输入参数的噪声函数。 对于二维噪声,我们会在网格顶点处分布随机值。噪声函数的2D版本以2D点作为输入,假设当前要求的点为pp。与一维版本类似,我们先得找到点pp在网格中的位置,如果点在网格之外,可以通过相同的取模技巧重新映射pp点的位置,得到网格上点pp的新坐标,记作PnoisePnoise。 之前的章节中讲过插值相关的很多内容。

1.jpg

如下图所示:点PnoisePnoise被一个单元上的四个顶点所包围。使用之前学到过的双线性插值技术可以轻松得到点PnoisePnoise的值(周围四个点的加权平均):

2.jpg

为此我们首先计算sts和t,它们是一维噪声中t的对应物:

3.png

代码实现如下:

    function lerp(min:number, max: nmber, t: number) {
        return min * (1-t) + max * t;
    }

    function smoothStep(t: number) {
        return t * t * (3- 2 * t);
    }

    class vec2 {
        public x: number;
        public y: number;
        constructor(x: number,y: number) {
            this.x = x;
            this.y = y;
        }
    }

    class ValueNoise {
        public K_MAX_TABLE_SIZE: number;
        public K_MAX_TABLE_SIZE_MASK: number;
        public vertices: number[];
        constructor(){
            this.K_MAX_TABLE_SIZE = 256;
            this.K_MAX_TABLE_SIZE_MASK = this.K_MAX_TABLE_SIZE - 1;
            this.vertices = [];
            for (let i = 0; i < this.K_MAX_TABLE_SIZE * this.K_MAX_TABLE_SIZE; i++) {
               this.vertices[i] = Math.random();
            }
        }

        computeX(p: vec2) {
            const xi = Math.floor(p.x);
            const yi = Math.floor(p.y);
            const s = p.x - xi;
            const t = p.y - yi;

            const rx0 = xi & this.K_MAX_TABLE_SIZE_MASK;
            const rx1 = (rx0 + 1) & this.K_MAX_TABLE_SIZE_MASK;
            const ry0 = yi & this.K_MAX_TABLE_SIZE_MASK;
            const ry1 = (ry0 + 1) & this.K_MAX_TABLE_SIZE_MASK;
            //通过置换表查找四个顶点处的随机值
            const c00 = this.vertices[ry0 * this.K_MAX_TABLE_SIZE_MASK + rx0];
            const c10 = this.vertices[ry0 * this.K_MAX_TABLE_SIZE_MASK + rx1];
            const c01 = this.vertices[ry1 * this.K_MAX_TABLE_SIZE_MASK + rx0];
            const c11 = this.vertices[ry1 * this.K_MAX_TABLE_SIZE_MASK + rx1];

            //对得到的s和t做平滑处理
            const ss = smoothStep(s);
            const st = smoothStep(t);
            //双线性插值得到p的值
            const a = lerp(c00, c10, ss);
            const b = lerp(c01, c11, ss);

            return lerp(a, b, st);
        }
    }

参考

Scratchapixel