• 【视觉基础篇】16 # 如何使用噪声生成复杂的纹理?


    说明

    【跟月影学可视化】学习笔记。

    什么是噪声?

    物理学上,噪声指一切不规则的信号(不一定要是声音),比如电磁噪声,热噪声,无线电传输时的噪声,激光器噪声,光纤通信噪声,照相机拍摄图片时画面的噪声等。

    如何实现噪声函数?

    我们知道随机数是离散的,如果对离散的随机点进行插值,可以让每个点之间的值连续过渡,然后使用 smoothstep 或者平滑的三次样条来插值,就可以形成一条连续平滑的随机曲线。

    对离散的随机值进行插值又被称为插值噪声Value Noise)。缺点:它的值的梯度不均匀。最直观的表现就是,二维噪声图像有明显的“块状”特点,不够平滑。

    DOCTYPE html>
    <html lang="en">
        <head>
            <meta charset="UTF-8" />
            <meta name="viewport" content="width=device-width, initial-scale=1.0" />
            <title>如何实现噪声函数title>
            <style>
                canvas {
                    border: 1px dashed salmon;
                }
            style>
        head>
        <body>
            <canvas width="512" height="512">canvas>
            <script src="./common/lib/gl-renderer.js">script>
            <script>
                const vertex = `
                    attribute vec2 a_vertexPosition;
                    attribute vec2 uv;
    
                    varying vec2 vUv;
    
                    void main() {
                        gl_PointSize = 1.0;
                        vUv = uv;
                        gl_Position = vec4(a_vertexPosition, 1, 1);
                    }
                `;
    
                const fragment = `
                    #ifdef GL_ES
                    precision highp float;
                    #endif
    
                    varying vec2 vUv;
    
                    // 随机函数
                    float random (float x) {
                        return fract(sin(x * 1243758.5453123));
                    }
    
                    void main() {
                        vec2 st = vUv - vec2(0.5);
                        st *= 10.0;
                        float i = floor(st.x);
                        float f = fract(st.x);
                        
                        // d直接等于随机函数返回值,这样d不连续
                        // float d = random(i);
                        // 线段的首尾就会连起来,得到一段连续的折线。
                        // float d = mix(random(i), random(i + 1.0), f);
                        // 下面两种都得到一条连续并且平滑的曲线
                        // float d = mix(random(i), random(i + 1.0), smoothstep(0.0, 1.0, f));
                        float d = mix(random(i), random(i + 1.0), f * f * (3.0 - 2.0 * f));
                        
                        gl_FragColor.rgb = (smoothstep(st.y - 0.05, st.y, d) - smoothstep(st.y, st.y + 0.05, d)) * vec3(1.0);
                        gl_FragColor.a = 1.0;
                    }
                `;
    
                const canvas = document.querySelector("canvas");
                const renderer = new GlRenderer(canvas);
                const program = renderer.compileSync(fragment, vertex);
                renderer.useProgram(program);
    
                renderer.setMeshData([
                    {
                        positions: [
                            [-1, -1],
                            [-1, 1],
                            [1, 1],
                            [1, -1],
                        ],
                        attributes: {
                            uv: [
                                [0, 0],
                                [0, 1],
                                [1, 1],
                                [1, 0],
                            ],
                        },
                        cells: [
                            [0, 1, 2],
                            [2, 0, 3],
                        ],
                    },
                ]);
    
                renderer.render();
            script>
        body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92

    在这里插入图片描述

    在 2D 中,除了在一条线的两点(fract(x) 和 fract(x)+1.0)中插值,我们将在一个平面上的方形的四角(fract(st), fract(st)+vec2(1.,0.), fract(st)+vec2(0.,1.) 和 fract(st)+vec2(1.,1.))中插值。https://thebookofshaders.com/11/?lan=ch

    在这里插入图片描述

    把 st 与方形区域的四个顶点(对应四个向量)做插值,这样就能得到二维噪声。

    DOCTYPE html>
    <html lang="en">
        <head>
            <meta charset="UTF-8" />
            <meta name="viewport" content="width=device-width, initial-scale=1.0" />
            <title>二维噪声title>
            <style>
                canvas {
                    border: 1px dashed salmon;
                }
            style>
        head>
        <body>
            <canvas width="512" height="512">canvas>
            <script src="./common/lib/gl-renderer.js">script>
            <script>
                const vertex = `
                    attribute vec2 a_vertexPosition;
                    attribute vec2 uv;
    
                    varying vec2 vUv;
    
                    void main() {
                        gl_PointSize = 1.0;
                        vUv = uv;
                        gl_Position = vec4(a_vertexPosition, 1, 1);
                    }
                `;
    
                const fragment = `
                    #ifdef GL_ES
                    precision highp float;
                    #endif
    
                    varying vec2 vUv;
    
                    float random (vec2 st) {
                        return fract(sin(dot(st.xy, vec2(12.9898,78.233)))*43758.5453123);
                    }
    
                    // 二维噪声,对st与方形区域的四个顶点插值
                    highp float noise(vec2 st) {
                        vec2 i = floor(st);
                        vec2 f = fract(st);
                        vec2 u = f * f * (3.0 - 2.0 * f);
                        return mix( mix( random( i + vec2(0.0,0.0) ),
                            random( i + vec2(1.0,0.0) ), u.x),
                            mix( random( i + vec2(0.0,1.0) ),
                            random( i + vec2(1.0,1.0) ), u.x), u.y);
                    }
    
                    void main() {
                        vec2 st = vUv * 20.0;
                        gl_FragColor.rgb = vec3(noise(st));
                        gl_FragColor.a = 1.0;
                    }
                `;
    
                const canvas = document.querySelector("canvas");
                const renderer = new GlRenderer(canvas);
                const program = renderer.compileSync(fragment, vertex);
                renderer.useProgram(program);
    
                renderer.setMeshData([
                    {
                        positions: [
                            [-1, -1],
                            [-1, 1],
                            [1, 1],
                            [1, -1],
                        ],
                        attributes: {
                            uv: [
                                [0, 0],
                                [0, 1],
                                [1, 1],
                                [1, 0],
                            ],
                        },
                        cells: [
                            [0, 1, 2],
                            [2, 0, 3],
                        ],
                    },
                ]);
    
                renderer.render();
            script>
        body>
    html>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91

    在这里插入图片描述

    噪声的应用

    实现类似于水滴滚过物体表面的效果

    结合噪声和距离场,来实现类似于水滴滚过物体表面的效果。

    DOCTYPE html>
    <html lang="en">
        <head>
            <meta charset="UTF-8" />
            <meta name="viewport" content="width=device-width, initial-scale=1.0" />
            <title>实现类似于水滴滚过物体表面的效果title>
            <style>
                canvas {
                    border: 1px dashed salmon;
                }
            style>
        head>
        <body>
            <canvas width="512" height="512">canvas>
            <script src="./common/lib/gl-renderer.js">script>
            <script>
                const vertex = `
                    attribute vec2 a_vertexPosition;
                    attribute vec2 uv;
    
                    varying vec2 vUv;
    
                    void main() {
                        gl_PointSize = 1.0;
                        vUv = uv;
                        gl_Position = vec4(a_vertexPosition, 1, 1);
                    }
                `;
    
                const fragment = `
                    #ifdef GL_ES
                    precision highp float;
                    #endif
    
                    varying vec2 vUv;
                    uniform float uTime;
                    
                    float random (vec2 st) {
                        return fract(sin(dot(st.xy, vec2(12.9898,78.233)))*43758.5453123);
                    }
    
                    highp float noise(vec2 st) {
                        vec2 i = floor(st);
                        vec2 f = fract(st);
                        vec2 u = f * f * (3.0 - 2.0 * f);
                        return mix( mix( random( i + vec2(0.0,0.0) ),
                            random( i + vec2(1.0,0.0) ), u.x),
                            mix( random( i + vec2(0.0,1.0) ),
                            random( i + vec2(1.0,1.0) ), u.x), u.y);
                    }
    
                    void main() {
                        vec2 st = mix(vec2(-10, -10), vec2(10, 10), vUv);
                        float d = distance(st, vec2(0));
                        d *= noise(uTime + st);
                        d = smoothstep(0.0, 1.0, d) - step(1.0, d);
                        gl_FragColor.rgb = vec3(d);
                        gl_FragColor.a = 1.0;
                    }
                `;
    
                const canvas = document.querySelector("canvas");
                const renderer = new GlRenderer(canvas);
                const program = renderer.compileSync(fragment, vertex);
                renderer.useProgram(program);
    
                renderer.setMeshData([
                    {
                        positions: [
                            [-1, -1],
                            [-1, 1],
                            [1, 1],
                            [1, -1],
                        ],
                        attributes: {
                            uv: [
                                [0, 0],
                                [0, 1],
                                [1, 1],
                                [1, 0],
                            ],
                        },
                        cells: [
                            [0, 1, 2],
                            [2, 0, 3],
                        ],
                    },
                ]);
    
                renderer.render();
    
                function update(t) {
                    renderer.uniforms.uTime = t / 1000;
                    requestAnimationFrame(update);
                }
    
                update(0);
            script>
        body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100

    在这里插入图片描述

    实现类似于木头的条纹

    使用不同的距离场构造方式,加上旋转噪声,构造出类似于木头的条纹。

    DOCTYPE html>
    <html lang="en">
        <head>
            <meta charset="UTF-8" />
            <meta name="viewport" content="width=device-width, initial-scale=1.0" />
            <title>实现类似于木头的条纹title>
            <style>
                canvas {
                    border: 1px dashed salmon;
                }
            style>
        head>
        <body>
            <canvas width="512" height="512">canvas>
            <script src="./common/lib/gl-renderer.js">script>
            <script>
                const vertex = `
                    attribute vec2 a_vertexPosition;
                    attribute vec2 uv;
    
                    varying vec2 vUv;
    
                    void main() {
                        gl_PointSize = 1.0;
                        vUv = uv;
                        gl_Position = vec4(a_vertexPosition, 1, 1);
                    }
                `;
    
                const fragment = `
                    #ifdef GL_ES
                    precision highp float;
                    #endif
    
                    varying vec2 vUv;
                    uniform float uTime;
                    
                    float random (vec2 st) {
                        return fract(sin(dot(st.xy, vec2(12.9898,78.233)))*43758.5453123);
                    }
    
                    highp float noise(vec2 st) {
                        vec2 i = floor(st);
                        vec2 f = fract(st);
                        vec2 u = f * f * (3.0 - 2.0 * f);
                        return mix( mix( random( i + vec2(0.0,0.0) ),
                            random( i + vec2(1.0,0.0) ), u.x),
                            mix( random( i + vec2(0.0,1.0) ),
                            random( i + vec2(1.0,1.0) ), u.x), u.y);
                    }
    
                    float lines(in vec2 pos, float b){
                        float scale = 10.0;
                        pos *= scale;
                        return smoothstep(0.0, 0.5 + b * 0.5, abs((sin(pos.x * 3.1415) + b * 2.0)) * 0.5);
                    }
    
                    vec2 rotate(vec2 v0, float ang) {
                        float sinA = sin(ang);
                        float cosA = cos(ang);
                        mat3 m = mat3(cosA, -sinA, 0, sinA, cosA, 0, 0, 0, 1);
                        return (m * vec3(v0, 1.0)).xy;
                    }
    
                    void main() {
                        vec2 st = vUv.yx * vec2(10.0, 3.0);
                        st = rotate(st, noise(st));
    
                        float d = lines(st, 0.5);
    
                        gl_FragColor.rgb = 1.0 - vec3(d);
                        gl_FragColor.a = 1.0;
                    }
                `;
    
                const canvas = document.querySelector("canvas");
                const renderer = new GlRenderer(canvas);
                const program = renderer.compileSync(fragment, vertex);
                renderer.useProgram(program);
    
                renderer.setMeshData([
                    {
                        positions: [
                            [-1, -1],
                            [-1, 1],
                            [1, 1],
                            [1, -1],
                        ],
                        attributes: {
                            uv: [
                                [0, 0],
                                [0, 1],
                                [1, 1],
                                [1, 0],
                            ],
                        },
                        cells: [
                            [0, 1, 2],
                            [2, 0, 3],
                        ],
                    },
                ]);
    
                renderer.render();
    
                function update(t) {
                    renderer.uniforms.uTime = t / 1000;
                    requestAnimationFrame(update);
                }
    
                update(0);
            script>
        body>
    html>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115

    在这里插入图片描述

    梯度噪声

    插值噪声的缺点可以使用另一种噪声算法来解决,梯度噪声是对随机的二维向量来插值,而不是一维的随机数。这样我们就能够获得更加平滑的噪声效果。

    可以参考这个例子:https://www.shadertoy.com/view/XdXGW8

    在这里插入图片描述

    DOCTYPE html>
    <html lang="en">
        <head>
            <meta charset="UTF-8" />
            <meta name="viewport" content="width=device-width, initial-scale=1.0" />
            <title>梯度噪声title>
            <style>
                canvas {
                    border: 1px dashed salmon;
                }
            style>
        head>
        <body>
            <canvas width="512" height="512">canvas>
            <script src="./common/lib/gl-renderer.js">script>
            <script>
                const vertex = `
                    attribute vec2 a_vertexPosition;
                    attribute vec2 uv;
    
                    varying vec2 vUv;
    
                    void main() {
                        gl_PointSize = 1.0;
                        vUv = uv;
                        gl_Position = vec4(a_vertexPosition, 1, 1);
                    }
                `;
    
                const fragment = `
                    #ifdef GL_ES
                    precision highp float;
                    #endif
    
                    varying vec2 vUv;
    
                    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);
                    }
    
                    // Gradient Noise by Inigo Quilez - iq/2013
                    // https://www.shadertoy.com/view/XdXGW8
                    float 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,0.0) ), f - vec2(0.0,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
                        );
                    }
    
                    void main() {
                        vec2 st = vUv * 20.0;
                        gl_FragColor.rgb = vec3(0.5 * noise(st) + 0.5);
                        gl_FragColor.a = 1.0;
                    }
                `;
    
                const canvas = document.querySelector("canvas");
                const renderer = new GlRenderer(canvas);
                const program = renderer.compileSync(fragment, vertex);
                renderer.useProgram(program);
    
                renderer.setMeshData([
                    {
                        positions: [
                            [-1, -1],
                            [-1, 1],
                            [1, 1],
                            [1, -1],
                        ],
                        attributes: {
                            uv: [
                                [0, 0],
                                [0, 1],
                                [1, 1],
                                [1, 0],
                            ],
                        },
                        cells: [
                            [0, 1, 2],
                            [2, 0, 3],
                        ],
                    },
                ]);
    
                renderer.render();
            script>
        body>
    html>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95

    在这里插入图片描述

    用噪声实现云雾效果

    Smooth HSVhttps://www.shadertoy.com/view/MsS3Wc

    在这里插入图片描述

    DOCTYPE html>
    <html lang="en">
        <head>
            <meta charset="UTF-8" />
            <meta name="viewport" content="width=device-width, initial-scale=1.0" />
            <title>用噪声实现云雾效果title>
            <style>
                canvas {
                    border: 1px dashed salmon;
                }
            style>
        head>
        <body>
            <canvas width="512" height="512">canvas>
            <script src="./common/lib/gl-renderer.js">script>
            <script>
                const vertex = `
                    attribute vec2 a_vertexPosition;
                    attribute vec2 uv;
    
                    varying vec2 vUv;
    
                    void main() {
                        gl_PointSize = 1.0;
                        vUv = uv;
                        gl_Position = vec4(a_vertexPosition, 1, 1);
                    }
                `;
    
                const fragment = `
                    #ifdef GL_ES
                    precision highp float;
                    #endif
    
                    varying vec2 vUv;
    
                    //  Function from Iñigo Quiles
                    //  https://www.shadertoy.com/view/MsS3Wc
                    vec3 hsb2rgb(vec3 c){
                        vec3 rgb = clamp(abs(mod(c.x*6.0+vec3(0.0,4.0,2.0), 6.0)-3.0)-1.0, 0.0, 1.0);
                        rgb = rgb * rgb * (3.0 - 2.0 * rgb);
                        return c.z * mix(vec3(1.0), rgb, c.y);
                    }
                    
                    float random (vec2 st) {
                        return fract(sin(dot(st.xy, vec2(12.9898,78.233)))*43758.5453123);
                    }
    
                    highp float noise(vec2 st) {
                        vec2 i = floor(st);
                        vec2 f = fract(st);
                        vec2 u = f * f * (3.0 - 2.0 * f);
                        return mix( mix( random( i + vec2(0.0,0.0) ),
                            random( i + vec2(1.0,0.0) ), u.x),
                            mix( random( i + vec2(0.0,1.0) ),
                            random( i + vec2(1.0,1.0) ), u.x), u.y
                        );
                    }
    
                    #define OCTAVES 6
                    float mist(vec2 st) {
                        //Initial values
                        float value = 0.0;
                        float amplitude = 0.5;
    
                        // Loop of octaves
                        for(int i = 0; i < OCTAVES; i++) {
                            value += amplitude * noise(st);
                            st *= 2.0;
                            amplitude *= 0.5;
                        }
                        return value;
                    }
    
                    uniform float uTime;
    
                    void main() {
                        vec2 st = vUv;
                        st.x += 0.1 * uTime; 
                        // gl_FragColor.rgb = vec3(mist(st));
                        gl_FragColor.rgb = hsb2rgb(vec3 (mist(st), 1.0, 1.0));
                        gl_FragColor.a = 1.0;
                    }
                `;
    
                const canvas = document.querySelector("canvas");
                const renderer = new GlRenderer(canvas);
                const program = renderer.compileSync(fragment, vertex);
                renderer.useProgram(program);
                renderer.uniforms.uTime = 0.0;
    
                renderer.setMeshData([
                    {
                        positions: [
                            [-1, -1],
                            [-1, 1],
                            [1, 1],
                            [1, -1],
                        ],
                        attributes: {
                            uv: [
                                [0, 0],
                                [0, 1],
                                [1, 1],
                                [1, 0],
                            ],
                        },
                        cells: [
                            [0, 1, 2],
                            [2, 0, 3],
                        ],
                    },
                ]);
    
                renderer.render();
    
                function update(t) {
                    renderer.uniforms.uTime = t / 1000;
                    requestAnimationFrame(update);
                }
    
                update(0);
            script>
        body>
    html>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126

    在这里插入图片描述

    Simplex Noise

    Simplex Noise 是 Ken Perlin 在 2001 年的 Siggraph 会议上展示的 Simplex Noise 算法。它有更低的计算复杂度和更少的乘法运算,并且可以用更少的计算量达到更高的维度,而且它制造出的噪声非常自然。

    Simplex Noise 与插值噪声以及梯度噪声的不同之处在于,它不是对四边形进行插值,而是对三角网格进行插值。

    如下图:

    在这里插入图片描述

    该算法的优点:

    • 它有着更低的计算复杂度和更少乘法计算。
    • 它可以用更少的计算量达到更高的维度。
    • 制造出的 noise 没有明显的人工痕迹。
    • 有着定义得很精巧的连续的 gradients(梯度),可以大大降低计算成本。
    • 特别易于硬件实现。
    DOCTYPE html>
    <html lang="en">
        <head>
            <meta charset="UTF-8" />
            <meta name="viewport" content="width=device-width, initial-scale=1.0" />
            <title>Simplex Noisetitle>
            <style>
                canvas {
                    border: 1px dashed salmon;
                }
            style>
        head>
        <body>
            <canvas width="512" height="512">canvas>
            <script src="./common/lib/gl-renderer.js">script>
            <script>
                const vertex = `
                    attribute vec2 a_vertexPosition;
                    attribute vec2 uv;
    
                    varying vec2 vUv;
    
                    void main() {
                        gl_PointSize = 1.0;
                        vUv = uv;
                        gl_Position = vec4(a_vertexPosition, 1, 1);
                    }
                `;
    
                const fragment = `
                    #ifdef GL_ES
                    precision highp float;
                    #endif
    
                    varying vec2 vUv;
    
                    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); }
                    
                    //
                    // Description : GLSL 2D simplex noise function
                    //      Author : Ian McEwan, Ashima Arts
                    //  Maintainer : ijm
                    //     Lastmod : 20110822 (ijm)
                    //     License :
                    //  Copyright (C) 2011 Ashima Arts. All rights reserved.
                    //  Distributed under the MIT License. See LICENSE file.
                    //  https://github.com/ashima/webgl-noise
                    //
                    float noise(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);
                    }
    
                    void main() {
                        vec2 st = vUv * 20.0;
                        gl_FragColor.rgb = vec3(0.5 * noise(st) + 0.5);
                        gl_FragColor.a = 1.0;
                    }
                `;
    
                const canvas = document.querySelector("canvas");
                const renderer = new GlRenderer(canvas);
                const program = renderer.compileSync(fragment, vertex);
                renderer.useProgram(program);
    
                renderer.setMeshData([
                    {
                        positions: [
                            [-1, -1],
                            [-1, 1],
                            [1, 1],
                            [1, -1],
                        ],
                        attributes: {
                            uv: [
                                [0, 0],
                                [0, 1],
                                [1, 1],
                                [1, 0],
                            ],
                        },
                        cells: [
                            [0, 1, 2],
                            [2, 0, 3],
                        ],
                    },
                ]);
    
                renderer.render();
            script>
        body>
    html>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147

    在这里插入图片描述

    网格噪声

    网格噪声就是将噪声与网格结合使用的一种纹理生成技术。目前被广泛应用的程序化纹理技术,用来生成随机网格类的视觉效果,可以用来模拟物体表面的晶格、晶体生长、细胞、微生物等等有趣的效果。

    DOCTYPE html>
    <html lang="en">
        <head>
            <meta charset="UTF-8" />
            <meta name="viewport" content="width=device-width, initial-scale=1.0" />
            <title>网格噪声title>
            <style>
                canvas {
                    border: 1px dashed salmon;
                }
            style>
        head>
        <body>
            <canvas width="512" height="512">canvas>
            <script src="./common/lib/gl-renderer.js">script>
            <script>
                const vertex = `
                    attribute vec2 a_vertexPosition;
                    attribute vec2 uv;
    
                    varying vec2 vUv;
    
                    void main() {
                        gl_PointSize = 1.0;
                        vUv = uv;
                        gl_Position = vec4(a_vertexPosition, 1, 1);
                    }
                `;
    
                const fragment = `
                    #ifdef GL_ES
                    precision highp float;
                    #endif
    
                    varying vec2 vUv;
                    uniform float uTime;
    
                    vec2 random2(vec2 st){
                        st = vec2( dot(st,vec2(127.1,311.7)), dot(st,vec2(269.5,183.3)) );
                        return fract(sin(st) * 43758.5453123);
                    }
    
                    void main() {
                        vec2 st = vUv * 10.0;
    
                        float d = 1.0;
                        vec2 i_st = floor(st);
                        vec2 f_st = fract(st);
    
                        vec2 p = random2(i_st);
                        d = distance(f_st, p);
                        gl_FragColor.rgb = vec3(d);
                        gl_FragColor.a = 1.0;
                    }
                `;
                
                const canvas = document.querySelector("canvas");
                const renderer = new GlRenderer(canvas);
                const program = renderer.compileSync(fragment, vertex);
                renderer.useProgram(program);
    
                renderer.setMeshData([
                    {
                        positions: [
                            [-1, -1],
                            [-1, 1],
                            [1, 1],
                            [1, -1],
                        ],
                        attributes: {
                            uv: [
                                [0, 0],
                                [0, 1],
                                [1, 1],
                                [1, 0],
                            ],
                        },
                        cells: [
                            [0, 1, 2],
                            [2, 0, 3],
                        ],
                    },
                ]);
    
                renderer.render();
            script>
        body>
    html>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89

    在这里插入图片描述

    上面每个网格是独立的,并且界限分明,可以计算特征点到当前网格的距离,以及计算它到周围相邻的 8 个网格的距离,然后取最小值去实现边界过渡更圆滑效果。

    DOCTYPE html>
    <html lang="en">
        <head>
            <meta charset="UTF-8" />
            <meta name="viewport" content="width=device-width, initial-scale=1.0" />
            <title>网格噪声title>
            <style>
                canvas {
                    border: 1px dashed salmon;
                }
            style>
        head>
        <body>
            <canvas width="512" height="512">canvas>
            <script src="./common/lib/gl-renderer.js">script>
            <script>
                const vertex = `
                    attribute vec2 a_vertexPosition;
                    attribute vec2 uv;
    
                    varying vec2 vUv;
    
                    void main() {
                        gl_PointSize = 1.0;
                        vUv = uv;
                        gl_Position = vec4(a_vertexPosition, 1, 1);
                    }
                `;
    
                // 圆滑版本
                const fragment = `
                    #ifdef GL_ES
                    precision highp float;
                    #endif
    
                    varying vec2 vUv;
                    uniform float uTime;
                    
                    vec2 random2(vec2 st){
                        st = vec2(dot(st,vec2(127.1,311.7)), dot(st,vec2(269.5,183.3)));
                        return fract(sin(st) * 43758.5453123);
                    }
    
                    void main() {
                        vec2 st = vUv * 10.0;
    
                        float d = 1.0;
                        vec2 i_st = floor(st);
                        vec2 f_st = fract(st);
    
                        for(int i = -1; i <= 1; i++) {
                            for(int j = -1; j <= 1; j++) {
                                vec2 neighbor = vec2(float(i), float(j));
                                vec2 p = random2(i_st + neighbor);
                                p = 0.5 + 0.5 * sin(uTime + 6.2831 * p);
                                d = min(d, distance(f_st, neighbor + p));
                            }
                        }
    
                        gl_FragColor.rgb = vec3(d) + step(d, 0.03);
                        gl_FragColor.a = 1.0;
                    }
                `;
    
                const canvas = document.querySelector("canvas");
                const renderer = new GlRenderer(canvas);
                const program = renderer.compileSync(fragment, vertex);
                renderer.useProgram(program);
    			renderer.uniforms.uTime = 0.0;
    
                renderer.setMeshData([
                    {
                        positions: [
                            [-1, -1],
                            [-1, 1],
                            [1, 1],
                            [1, -1],
                        ],
                        attributes: {
                            uv: [
                                [0, 0],
                                [0, 1],
                                [1, 1],
                                [1, 0],
                            ],
                        },
                        cells: [
                            [0, 1, 2],
                            [2, 0, 3],
                        ],
                    },
                ]);
    
                renderer.render();
            script>
        body>
    html>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98

    在这里插入图片描述

    基于这个圆滑版本我们实现一下细胞动画效果

    DOCTYPE html>
    <html lang="en">
        <head>
            <meta charset="UTF-8" />
            <meta name="viewport" content="width=device-width, initial-scale=1.0" />
            <title>网格噪声模拟生物细胞title>
            <style>
                canvas {
                    border: 1px dashed salmon;
                }
            style>
        head>
        <body>
            <canvas width="512" height="512">canvas>
            <script src="./common/lib/gl-renderer.js">script>
            <script>
                const vertex = `
                    attribute vec2 a_vertexPosition;
                    attribute vec2 uv;
    
                    varying vec2 vUv;
    
                    void main() {
                        gl_PointSize = 1.0;
                        vUv = uv;
                        gl_Position = vec4(a_vertexPosition, 1, 1);
                    }
                `;
                
                const fragment = `
                    #ifdef GL_ES
                    precision highp float;
                    #endif
    
                    varying vec2 vUv;
                    uniform float uTime;
                    
                    vec2 random2(vec2 st){
                        st = vec2(dot(st,vec2(127.1,311.7)), dot(st,vec2(269.5,183.3)));
                        return fract(sin(st) * 43758.5453123);
                    }
    
                    void main() {
                        vec2 st = vUv * 10.0;
    
                        float d = 1.0;
                        vec2 i_st = floor(st);
                        vec2 f_st = fract(st);
    
                        for(int i = -1; i <= 1; i++) {
                            for(int j = -1; j <= 1; j++) {
                                vec2 neighbor = vec2(float(i), float(j));
                                vec2 p = random2(i_st + neighbor);
                                p = 0.5 + 0.5 * sin(uTime + 6.2831 * p);
                                d = min(d, distance(f_st, neighbor + p));
                            }
                        }
    
                        gl_FragColor.rgb = vec3(d) + step(d, 0.03);
                        gl_FragColor.a = 1.0;
                    }
                `;
    
                const canvas = document.querySelector("canvas");
                const renderer = new GlRenderer(canvas);
                const program = renderer.compileSync(fragment, vertex);
                renderer.useProgram(program);
                renderer.uniforms.uTime = 0.0;
    
                renderer.setMeshData([
                    {
                        positions: [
                            [-1, -1],
                            [-1, 1],
                            [1, 1],
                            [1, -1],
                        ],
                        attributes: {
                            uv: [
                                [0, 0],
                                [0, 1],
                                [1, 1],
                                [1, 0],
                            ],
                        },
                        cells: [
                            [0, 1, 2],
                            [2, 0, 3],
                        ],
                    },
                ]);
    
                renderer.render();
    
                function update(t) {
                    renderer.uniforms.uTime = t / 1000;
                    requestAnimationFrame(update);
                }
    
                update(0);
            script>
        body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103

    在这里插入图片描述

    拓展

    网格噪声(Cellular Noise):https://thebookofshaders.com/12/?lan=ch

    演示例子:https://thebookofshaders.com/edit.php#12/vorono-01.frag

    在这里插入图片描述

  • 相关阅读:
    new CCDIKSolver( OOI.kira, iks ); // 创建逆运动学求解器
    电力移动应用及终端安全溯源管控技术研究与实践
    2022 极术通讯-Arm 虚拟硬件加速物联网软件开发
    STC51单片机学习笔记4——stc12c56 串口
    数据仓库任务调度器-Azkaban | 案例测试<自动邮件报警通知机制>
    Rust学习日记(一)Cargo的使用
    【C语言必知必会| 第十篇】指针入门,这一篇就够了
    yolov1模型
    【JavaEE基础与高级 第55章】Java中的对象流详细介绍与使用
    大厂面试总被刨根问底?原来是性能优化不过关
  • 原文地址:https://blog.csdn.net/kaimo313/article/details/126834168