shder-控制生成-噪声

219 阅读11分钟

noise算法的核心是下面的代码。

float i = floor(x);  // 整数(i 代表 integer)
float f = fract(x);  // 小数(f 代表 fraction)
y = rand(i); //rand() 阶梯函数
//y = mix(rand(i), rand(i + 1.0), f); 线性插值
//y = mix(rand(i), rand(i + 1.0), smoothstep(0.,1.,f)); 平滑差值

image.png阶梯函数(图1)

使用多项式

float u = f * f * (3.0 - 2.0 * f ); // custom cubic curve 
y = mix(rand(i), rand(i + 1.0), u); // using it in the interpolation

1.二维噪声

#ifdef GL_ES
precision mediump float;
#endif

// 以下为Shader中使用到的uniform变量,由外部程序传入
uniform vec2 u_resolution; // 屏幕分辨率,vec2类型表示二维向量
uniform vec2 u_mouse; // 鼠标当前位置,vec2类型表示二维向量
uniform float u_time; // 经过的时间,float类型

// 2D Random
// 该函数用于生成二维随机值,接受一个二维向量st作为参数
// 返回值为一个在0到1之间的浮点数
float random(in vec2 st) {
    return fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453123);
}

// 2D Noise based on Morgan McGuire @morgan3d
// https://www.shadertoy.com/view/4dS3Wd
// 这是一个基于Morgan McGuire提供的噪声函数的二维噪声实现
float noise(in vec2 st) {
    vec2 i = floor(st); // 向下取整,获取当前点所处的二维格子坐标
    vec2 f = fract(st); // 分数部分,获取当前点在格子内的相对位置

    // Four corners in 2D of a tile
    // 获取当前格子四个顶点的随机值
    float a = random(i);
    float b = random(i + vec2(1.0, 0.0));
    float c = random(i + vec2(0.0, 1.0));
    float d = random(i + vec2(1.0, 1.0));

    // Smooth Interpolation

    // Cubic Hermine Curve.  Same as SmoothStep()
    // 使用Cubic Hermite曲线进行平滑插值,计算f*f*(3.0-2.0*f),得到u
    vec2 u = f * f * (3.0 - 2.0 * f);
    // 可以使用下面的注释代码替代上面一行,效果是一样的
    // vec2 u = smoothstep(0., 1., f);

    // Mix 4 corners percentages
    // 使用插值将四个顶点的随机值混合,得到最终的噪声值
    return mix(a, b, u.x) + (c - a) * u.y * (1.0 - u.x) + (d - b) * u.x * u.y;
}

void main() {
    vec2 st = gl_FragCoord.xy / u_resolution.xy; // 获取像素位置坐标,转换到0到1之间的范围

    // Scale the coordinate system to see
    // some noise in action
    vec2 pos = vec2(st * 5.0); // 将坐标缩放,增加噪声的频率

    // Use the noise function
    // 使用noise函数生成噪声,并将噪声值作为颜色值输出
    float n = noise(pos);

    gl_FragColor = vec4(vec

结果为。

image.png噪声(图2)

### 2.渐变噪声
#ifdef GL_ES
precision mediump float;
#endif

// 以下为Shader中使用到的uniform变量,由外部程序传入
uniform vec2 u_resolution; // 屏幕分辨率,vec2类型表示二维向量
uniform vec2 u_mouse; // 鼠标当前位置,vec2类型表示二维向量
uniform float u_time; // 经过的时间,float类型

// 生成二维随机向量
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
// 使用Inigo Quilez的渐变噪声算法
float noise(vec2 st) {
    vec2 i = floor(st); // 向下取整,获取当前点所处的二维格子坐标
    vec2 f = fract(st); // 分数部分,获取当前点在格子内的相对位置

    vec2 u = f*f*(3.0-2.0*f); // 使用Cubic Hermite曲线进行平滑插值,计算f*f*(3.0-2.0*f),得到u

    // 在四个格子顶点的噪声值和相对位置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 = gl_FragCoord.xy/u_resolution.xy; // 获取像素位置坐标,转换到0到1之间的范围
    st.x *= u_resolution.x/u_resolution.y; // 调整x坐标,使噪声在宽屏幕上也能正常显示
    vec3 color = vec3(0.0); // 初始化颜色为黑色

    vec2 pos = vec2(st*10.0); // 缩放坐标,增加噪声的频率

    color = vec3( noise(pos)*.5+.5 ); // 使用噪声函数生成噪声,并将噪声值映射到0到1之间的范围

    gl_FragColor = vec4(color,1.0); // 输出的颜色为(color, 1),即使用噪声值作为RGB颜色值,透明度为1
}

结果为。

image.png渐变噪声(图4)

3.旋涡效果

#ifdef GL_ES
precision mediump float;
#endif

// 以下为Shader中使用到的uniform变量,由外部程序传入
uniform vec2 u_resolution; // 屏幕分辨率,vec2类型表示二维向量
uniform vec2 u_mouse; // 鼠标当前位置,vec2类型表示二维向量
uniform float u_time; // 经过的时间,float类型

// 2D Random
// 该函数用于生成二维随机值,接受一个二维向量st作为参数
// 返回值为一个在0到1之间的浮点数
float random(in vec2 st) {
    return fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453123);
}

// Value noise by Inigo Quilez - iq/2013
// 使用Inigo Quilez的Value Noise算法
// https://www.shadertoy.com/view/lsf3WH
float noise(vec2 st) {
    vec2 i = floor(st); // 向下取整,获取当前点所处的二维格子坐标
    vec2 f = fract(st); // 分数部分,获取当前点在格子内的相对位置
    vec2 u = f * f * (3.0 - 2.0 * f); // 使用Cubic Hermite曲线进行平滑插值,计算f*f*(3.0-2.0*f),得到u

    // 在四个格子顶点的噪声值和相对位置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);
}

// 2x2 2D Rotation Matrix
// 用于旋转二维坐标点
mat2 rotate2d(float angle) {
    return mat2(cos(angle), -sin(angle),
                sin(angle), cos(angle));
}

// 生成线条效果
float lines(in vec2 pos, float b) {
    float scale = 10.0; // 缩放因子,控制线条的频率
    pos *= scale; // 缩放坐标,增加线条的频率
    return smoothstep(0.0, .5 + b * .5, abs((sin(pos.x * 3.1415) + b * 2.0)) * .5);
}

void main() {
    vec2 st = gl_FragCoord.xy / u_resolution.xy; // 获取像素位置坐标,转换到0到1之间的范围
    st.y *= u_resolution.y / u_resolution.x; // 调整y坐标,使图案在宽屏幕上也能正常显示

    vec2 pos = st.yx * vec2(10., 3.); // 缩放坐标,增加图案的频率和形状

    float pattern = pos.x; // 使用x坐标作为图案的起始值

    // Add noise
    // 在图案上应用噪声
    pos = rotate2d(noise(pos)) * pos;

    // Draw lines
    // 生成线条效果
    pattern = lines(pos, .5);

    gl_FragColor = vec4(vec3(pattern), 1.0); // 输出的颜色为(pattern, pattern, pattern, 1),即使用图案值作为RGB颜色值,透明度为1
}

结果为。

image.png旋涡效果(图5)

4.基于距离场的漩涡效果

#ifdef GL_ES
precision mediump float;
#endif

// 以下为Shader中使用到的uniform变量,由外部程序传入
uniform vec2 u_resolution; // 屏幕分辨率,vec2类型表示二维向量
uniform vec2 u_mouse; // 鼠标当前位置,vec2类型表示二维向量
uniform float u_time; // 经过的时间,float类型

// 生成二维随机向量
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
// 使用Inigo Quilez的渐变噪声算法
float noise(vec2 st) {
    vec2 i = floor(st); // 向下取整,获取当前点所处的二维格子坐标
    vec2 f = fract(st); // 分数部分,获取当前点在格子内的相对位置
    vec2 u = f*f*(3.0-2.0*f); // 使用Cubic Hermite曲线进行平滑插值,计算f*f*(3.0-2.0*f),得到u

    // 在四个格子顶点的噪声值和相对位置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 = gl_FragCoord.xy / u_resolution.xy; // 获取像素位置坐标,转换到0到1之间的范围
    st.x *= u_resolution.x / u_resolution.y; // 调整x坐标,使图像在宽屏幕上也能正常显示
    vec3 color = vec3(0.0); // 初始化颜色为黑色

    float t = 1.0;
    // 解除下一行的注释可以启用动画效果
    // t = abs(1.0 - sin(u_time * 0.1)) * 5.0;
    // 将下面的注释取消,并注释/解注上面的几行代码:

    st += noise(st * 2.0) * t; // 使用噪声函数对坐标进行变换,产生动画效果
    color = vec3(1.0) * smoothstep(0.18, 0.2, noise(st)); // 大黑色液滴
    color += smoothstep(0.15, 0.2, noise(st * 10.0)); // 黑色喷溅
    color -= smoothstep(0.35, 0.4, noise(st * 10.0)); // 喷溅上的孔洞

    gl_FragColor = vec4(1.0 - color, 1.0); // 输出的颜色为(1-color.r, 1-color.g, 1-color.b, 1),即对图案的颜色进行反转,透明度为1
}

效果为。

image.png基于距离场的漩涡(图6)

5.形状绘制

#ifdef GL_ES
precision mediump float;
#endif

// 以下为Shader中使用到的uniform变量,由外部程序传入
uniform vec2 u_resolution; // 屏幕分辨率,vec2类型表示二维向量
uniform vec2 u_mouse; // 鼠标当前位置,vec2类型表示二维向量
uniform float u_time; // 经过的时间,float类型

// 生成二维随机向量
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
// 使用Inigo Quilez的渐变噪声算法
float noise(vec2 st) {
    vec2 i = floor(st); // 向下取整,获取当前点所处的二维格子坐标
    vec2 f = fract(st); // 分数部分,获取当前点在格子内的相对位置
    vec2 u = f*f*(3.0-2.0*f); // 使用Cubic Hermite曲线进行平滑插值,计算f*f*(3.0-2.0*f),得到u

    // 在四个格子顶点的噪声值和相对位置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);
}

// 2x2 2D Rotation Matrix
// 用于旋转二维坐标点
mat2 rotate2d(float _angle) {
    return mat2(cos(_angle), -sin(_angle),
                sin(_angle), cos(_angle));
}

// 绘制图形的函数
// 参数st为像素位置坐标,radius为波浪的半径
float shape(vec2 st, float radius) {
    st = vec2(0.5) - st; // 将坐标系原点移动到中心位置
    float r = length(st) * 2.0; // 计算像素点到中心的距离,乘以2是为了放大效果
    float a = atan(st.y, st.x); // 计算像素点与中心的角度
    float m = abs(mod(a + u_time * 2.0, 3.14 * 2.0) - 3.14) / 3.6; // 计算角度的周期性波动
    float f = radius; // 设置波浪的初始半径
    m += noise(st + u_time * 0.1) * 0.5; // 在波浪上添加噪声,使波浪变化
    // a *= 1. + abs(atan(u_time*0.2)) * 0.1; // 添加角度的变化,可以解除注释来尝试不同效果
    // a *= 1. + noise(st + u_time * 0.1) * 0.1; // 添加噪声的角度变化,可以解除注释来尝试不同效果
    f += sin(a * 50.) * noise(st + u_time * 0.2) * 0.1; // 在波浪上添加噪声,使波浪变化
    f += (sin(a * 20.) * 0.1 * pow(m, 2.)); // 添加随时间变化的波浪形状
    return 1. - smoothstep(f, f + 0.007, r); // 使用平滑阶梯函数来绘制波浪形状
}

// 绘制波浪形状的边界
// 参数st为像素位置坐标,radius为波浪的半径,width为边界宽度
float shapeBorder(vec2 st, float radius, float width) {
    return shape(st, radius) - shape(st, radius - width); // 绘制波浪形状的边界
}

void main() {
    vec2 st = gl_FragCoord.xy / u_resolution.xy; // 获取像素位置坐标,转换到0到1之间的范围
    vec3 color

结果为。

image.png形状(图7)

6.噪声优化三角形划分

#ifdef GL_ES
precision mediump float;
#endif

// 以下为Shader中使用到的uniform变量,由外部程序传入
uniform vec2 u_resolution; // 屏幕分辨率,vec2类型表示二维向量
uniform vec2 u_mouse; // 鼠标当前位置,vec2类型表示二维向量
uniform float u_time; // 经过的时间,float类型

// 2D网格的偏移函数
vec2 skew(vec2 st) {
    vec2 r = vec2(0.0);
    r.x = 1.1547 * st.x; // 设置x轴方向的偏移,用于生成等边三角形网格
    r.y = st.y + 0.5 * r.x; // 设置y轴方向的偏移,用于生成等边三角形网格
    return r;
}

// 生成2D简单网格的函数
vec3 simplexGrid(vec2 st) {
    vec3 xyz = vec3(0.0);

    vec2 p = fract(skew(st)); // 将输入坐标通过skew函数进行偏移,得到分数部分
    if (p.x > p.y) {
        xyz.xy = 1.0 - vec2(p.x, p.y - p.x);
        xyz.z = p.y;
    } else {
        xyz.yz = 1.0 - vec2(p.x - p.y, p.y);
        xyz.x = p.x;
    }

    return fract(xyz); // 将xyz向量的各个分量映射到[0, 1]范围内
}

void main() {
    vec2 st = gl_FragCoord.xy / u_resolution.xy; // 获取像素位置坐标,转换到0到1之间的范围
    vec3 color = vec3(0.0);

    // 缩放空间以查看网格
    st *= 10.0;

    // 显示二维网格,颜色由像素位置决定
    color.rg = fract(st);

    // 使用偏移函数skew展示二维网格,颜色由像素位置决定
    color.rg = fract(skew(st));

    // 将网格细分为等边三角形
    color = simplexGrid(st);

    gl_FragColor = vec4(color, 1.0); // 输出的颜色为(color.r, color.g, color.b, 1),即网格颜色,透明度为1
}

效果如下。

image.png三角优化(图8)

7.流体效果

#ifdef GL_ES
precision mediump float;
#endif

// 以下为Shader中使用到的uniform变量,由外部程序传入
uniform vec2 u_resolution; // 屏幕分辨率,vec2类型表示二维向量
uniform vec2 u_mouse; // 鼠标当前位置,vec2类型表示二维向量
uniform float u_time; // 经过的时间,float类型

// 用于将vec3向量的每个分量映射到[0, 289)范围内
vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }

// 用于将vec2向量的每个分量映射到[0, 289)范围内
vec2 mod289(vec2 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }

// 使用一系列操作和参数对输入向量x进行排列和计算,返回vec3类型结果
vec3 permute(vec3 x) { return mod289(((x * 34.0) + 1.0) * x); }

// 2维Simplex噪声函数
float snoise(vec2 v) {
    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
    vec2 i = floor(v + dot(v, C.yy));
    vec2 x0 = v - i + dot(i, C.xx);
    vec2 i1;
    i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
    vec4 x12 = x0.xyxy + C.xxzz;
    x12.xy -= i1;
    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(x12.xy, x12.xy), dot(x12.zw, x12.zw)), 0.0);
    m = m * m;
    m = m * m;
    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;
    m *= 1.79284291400159 - 0.85373472095314 * (a0 * a0 + h * h);
    vec3 g;
    g.x = a0.x * x0.x + h.x * x0.y;
    g.yz = a0.yz * x12.xz + h.yz * x12.yw;
    return 130.0 * dot(m, g); // 返回最终的噪声值
}

void main() {
    vec2 st = gl_FragCoord.xy / u_resolution.xy; // 获取像素位置坐标,转换到0到1之间的范围
    st.x *= u_resolution.x / u_resolution.y; // 根据屏幕分辨率调整x坐标,避免变形
    vec3 color = vec3(0.0); // 初始化颜色为黑色
    vec2 pos = vec2(st * 3.); // 缩放空间以查看噪声效果

    float DF = 0.0; // 初始化密度因子为0

    // 添加一个随机位置
    float a = 0.0; // 用于储存角度
    vec2 vel = vec2(u_time * 0.1); // 用时间作为速度的x分量
    DF += snoise(pos + vel) * 0.25 + 0.25; // 根据随机位置和速度计算噪声值,并将结果加到密度因子上

    // 添加另一个随机位置
    a = snoise(pos * vec2(cos(u_time * 0.15), sin(u_time * 0.1)) * 0.1) * 3.1415; // 计算另一个角度
    vel = vec2(cos(a), sin(a)); // 根据角度计算速度
    DF += snoise(pos + vel) * 0.25 + 0.25; // 根据随机位置和速度计算噪声值,并将结果加到密度因子上

    // 将密度因子作为颜色,添加颜色的过渡效果
    color = vec3(smoothstep(0.7, 0.75, fract(DF)));

    gl_FragColor = vec4(1.0 - color, 1.0); // 输出的颜色为(1-color.r, 1-color.g, 1-color.b, 1),即对颜色进行反转,透明度为1
}

效果如下。

image.png流体(图9)