ODOShader

ODOS丨吵吵儿 MAKE SOME NOISES🔇 - 1

by ERIN.Z, 2022-01-30


portal

写在前面

是iq,是iq啊啊啊啊啊!!!!!! QAQQQQQ I love it😭 I love it😭 I love it😭 I love it😭 I love it😭 我现在就把这条评论裱起来😭😭😭😭😭

NOISE

不知道是不是只有北京有“吵(chāo)吵儿”(?)这个说法...总之今天的内容是关于制造噪声,嗯,视觉上那种。 White Noise就是最闹眼的,like电视花屏。每个像素的取值是完全随机的,没有相关性,概率上平均分布。 WhiteNoise

In signal processing, white noise is a random signal having equal intensity at different frequencies, giving it a constant power spectral density.

而为了生成更加贴近大自然的随机效果,前人们提出了Perlin(用于模拟自然环境,如火焰、云朵等)、Worley(用于模拟多孔结构,如纸张、木纹等)等噪声算法。 根据乐乐老师说根据wiki,由程序产生噪声的方法大致可分为两大类: 类别 名称
基于晶格的方法(Lattice based) 又可细分为两种:
第一种是梯度噪声(Gradient noise),包括Perlin噪声, Simplex噪声,Wavelet噪声等;
第二种是Value噪声(Value noise)。
基于点的方法(Point based) Worley噪声

常见的说法还有分形噪声(Fractal Noise)和fbm分形布朗运动(Fractal Brownian Motion):

分形噪声会把多个不同振幅、不同频率的octave相叠加,得到一个更加自然的噪声。而这些octave则对应了不同的来源,它可以是Gradient噪声(例如Perlin噪声)或Value噪声,也可以是一个简单的白噪声(White noise)。 basic noise

Lattice Noise

lattice noise 进入到最常用的perlin noise之前,还是要为自己的菜打打基础。 之前图形学的作业里,尝试总结了一下:

  • 生成噪波需要构造一个noise函数,该函数的输入为纹理坐标,把坐标转换成能够作为伪随机数(PRN)使用的hash值;或者能把hash值作为索引,在生成的PRN表中查找对应数值。 以下实现以Lattice Noise为例。
  • 首先声明一个利用white noise生成的伪随机数(PRN)表,该表包括从0到255的所有数,在数组中随机排布。
  • Noise函数首先对坐标进行取整,通过PERM(x+PERM(y+PERM(z)))获得格点的PRN值,再除以256以获得格点处的灰度值。其中PERM函数的作用是先对输入值与256取余,然后于perm数组中取PRN值。之后再对每个格点进行插值即为Value Noise。 详细实现 emmmmmm.....对也不对。 基于lattice的噪音生成算法都需要为整数格点(integer lattice)生成PRN,每一个像素对所处晶格的四个角点进行插值。生成PRN的内容和插值的方式形成了不同的噪声效果。 最简单粗暴的Value Noise就是在每个格点生成0~1的PRN,然后直接插值。 Value noise

Perlin Noise

Value noise即便使用三次插值、catmull-rom等比较平滑的插值方式,还是能很辨认出原始的晶格轮廓,有明显的网格感。 为了消除这种块状的效果,在1985年Ken Perlin开发了另一种noise算法 Gradient Noise,后来也被称为Pelrin Noise。解决了如何插入随机的gradients而不是一个固定值。这些梯度值来自于一个二维的随机函数,返回一个方向(vec2 格式的向量),而不仅是一个值(float格式)。

概括来说,Perlin噪声的实现需要三个步骤:

  • 定义一个晶格结构,每个晶格的顶点有一个“伪随机”的梯度向量(其实就是个向量啦)。对于二维的Perlin噪声来说,晶格结构就是一个平面网格,三维的就是一个立方体网格。
  • 输入一个点(二维的话就是二维坐标,三维就是三维坐标,n维的就是n个坐标),我们找到和它相邻的那些晶格顶点(二维下有4个,三维下有8个,n维下有2^n个),计算该点到各个晶格顶点的距离向量,再分别与顶点上的梯度向量做点乘,得到2^n个点乘结果。
  • 使用缓和曲线(ease curves)来计算它们的权重和。在原始的Perlin噪声实现中,缓和曲线是s(t)=3t^2−2t^3(yep就是smoothstep那个曲线),在2002年的论文6中,Perlin改进为s(t)=6t^5−15t^4+10t^3。这里简单解释一下,为什么不直接使用s(t)=t,即线性插值。直接使用的线性插值的话,它的一阶导在晶格顶点处(即t = 0或t = 1)不为0,会造成明显的不连续性。s(t)=3t^2−2t^3在一阶导满足连续性,s(t)=6t^5−15t^4+10t^3在二阶导上仍然满足连续性。 Perlin Noise 这个算法与Value Noise的主要差别是:
    1. 晶格点上的随机取值由一维的随机值换成了二维的随机向量(也就是所谓的梯度向量);
    2. 插值的四个值(2D)是四个晶格点的梯度向量 和 距像素点的距离向量 点乘的结果。 原始算法中梯度向量的产生使用了蒙特卡洛算法,筛选单位圆(球)内的向量。shader实现时可以简化一下,乐乐老师这个版本没有归一化到单位圆内,是-1~1正方形内的随机向量:
 vec2 hash22(vec2 p){
    p = vec2( dot(p,vec2(127.1,311.7)),
              dot(p,vec2(269.5,183.3)));
    return -1.0 + 2.0 * fract(sin(p)*43758.5453123);
  }
  float perlin_noise(vec2 p)
  {
    vec2 pi = floor(p);
    vec2 pf = p - pi;
    vec2 w = pf * pf * (3.0 - 2.0 * pf);

    return mix(mix(dot(hash22(pi + vec2(0.0, 0.0)), pf - vec2(0.0, 0.0)), 
                   dot(hash22(pi + vec2(1.0, 0.0)), pf - vec2(1.0, 0.0)), w.x), 
               mix(dot(hash22(pi + vec2(0.0, 1.0)), pf - vec2(0.0, 1.0)), 
                   dot(hash22(pi + vec2(1.0, 1.0)), pf - vec2(1.0, 1.0)), w.x),
               w.y);
  }//-1~1
  float perlin_basic(vec2 p, float dens){
    return 0.5+perlin_noise(p*dens)*0.5;
  }//remap to 0~1

Perlin Noise

通过对perlin noise 进行fbm,可以获得更富有变化的效果。叠加时每一次噪声的采样频率翻倍,而振幅减少一倍。 fbm float perlin_fbm(vec2 p,float dens){ float f = 0.0; p = p dens; f += 1.0000 perlin_noise(p); p = 2.0 p; f += 0.5000 perlin_noise(p); p = 2.0 p; f += 0.2500 perlin_noise(p); p = 2.0 p; f += 0.1250 perlin_noise(p); p = 2.0 p; f += 0.0625 perlin_noise(p); p = 2.0 p; return f0.5+0.5; } perlin fbm 其他变体: perlrin abs fbm取绝对值,Perlin把这个公式称为turbulence。。由于进行了绝对值操作,因此会在0值变化处出现不连续性,形成一些尖锐的效果。通过合适的颜色叠加,我们可以用这种噪声来模拟火焰、云朵这些物体。 perlin abs 在之前turbulence公式的基础上使用了一个关于表面x分量的正弦函数,这个公式可以让表面沿着x方向形成一个条纹状的结构。Perlin使用这个公式模拟了一些大理石材质。 perlin sinx float perlin_sinx(vec2 p,float dens,float xscale){ float f = perlin_abs(p,dens); return sin(f 1.5 + p.x xscale); } perlin sinx

应用一下

如果希望实现二维等噪音动画效果,需要使用三维的噪音,第三维对应时间变量。在shadertoy上魔改了一个传送门: portal float snoise(vec3 uv, float res) { const vec3 s = vec3(1e0, 1e2, 1e3); uv = res; vec3 uv0 = floor(mod(uv, res))s; vec3 uv1 = floor(mod(uv+vec3(1.), res))*s;

    vec3 f = fract(uv); f = f*f*(3.0-2.0*f);

    vec4 v = vec4(uv0.x+uv0.y+uv0.z, uv1.x+uv0.y+uv0.z,
                  uv0.x+uv1.y+uv0.z, uv1.x+uv1.y+uv0.z);

    vec4 r = fract(sin(v*1e-1)*1e3);
    float r0 = mix(mix(r.x, r.y, f.x), mix(r.z, r.w, f.x), f.y);

    r = fract(sin((v + uv1.z - uv0.z)*1e-1)*1e3);
    float r1 = mix(mix(r.x, r.y, f.x), mix(r.z, r.w, f.x), f.y);

    return mix(r0, r1, f.z)*2.-1.;
}

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

    float color = 6.0 - (6.*length(2.*p));

    vec3 coord = vec3(atan(p.x,p.y)/6.2832+.5, length(p)*.4, .5);

    for(int i = 1; i <= 7; i++)
    {
        float power = pow(2.0, float(i));
        color += (1.5 / power) * snoise(coord + vec3(0.,-iTime*.05, iTime*.01), power*16.);
    }
    fragColor = vec4( sin(pow(max(color,0.),0.8))*(sin(iTime)*.1+.9)*.8,
                      sin(pow(max(color,.0),0.87))*(sin(iTime)*.1+.9)*.9, 
                      (sin(pow(max(color,0.),0.78)))*(sin(iTime*2.)*.1+.9)*1., 
                      1.0);
}

参考资料:

by ERIN.Z

2025 © typecho & elise