Shader特效——实现“噪声”【基于ShaderToy】【GLSL】
2016-08-01 23:51
676 查看
本文是学习了CandyCat的博客之后写的一个小结,女神的博客理论写得非常详尽,看完有种如沐春风的感觉。
从左到右依次为:1.Perlin噪声,2.FBM叠加的分形噪声,3.对FBM绝对值叠加的分形噪声,4.值噪声,5. Simplex噪声。
以下代码是基于ShaderToy的作者Inigo Quilez的demo进行二次修改的,并添加了自己的总结注释
注意:
1.fragCoord的取值范围是[vec2(0.0, 0.0), iResolution.xy];
2.Simplex噪声最后归一化所使用的最大值70.0,是当输入点在某一边中点时取得,此时|a| = √2/2, |b| = |c| = 1/√6,得h=(0,1/3,1/3)。取最大值时,梯度和距离向量的点乘结果可以认为是两者模的乘积,而梯度模的最大值为√2,因此最后和的最大值为:
134⋅16√⋅2√⋅2≈170
即 n 中第二分量与第三分量的和。
因此,我们最后把结果乘以70。那么,如果r^2取0.6,最后需要大约乘以为24.51。利用这个想法,我们可以在任意维度下计算最后的伸缩值,例如在三维下,单形,即正四面体的边长为√3/2,当r2取0.5时,最后大概需要乘以31.32。
GLSL Fragment代码和我总结的注释如下:
注:
z = atan(x, y)/6.2832+.5 ; x, y ∈(-1, 1).
z = length(p)*.4;
z = f*f*(3.0-2.0*f); f∈(0, 1) 相当于GLSL 的 smoothstep
1.若干常见噪声类型
先上个效果图:从左到右依次为:1.Perlin噪声,2.FBM叠加的分形噪声,3.对FBM绝对值叠加的分形噪声,4.值噪声,5. Simplex噪声。
以下代码是基于ShaderToy的作者Inigo Quilez的demo进行二次修改的,并添加了自己的总结注释
// Created by inigo quilez - iq/2013 // License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. // Gradient Noise (http://en.wikipedia.org/wiki/Gradient_noise), not to be confused with // Value Noise, and neither with Perlin's Noise (which is one form of Gradient Noise) // is probably the most convenient way to generate noise (a random smooth signal with // mostly all its energy in the low frequencies) suitable for procedural texturing/shading, // modeling and animation. // // It produces smoother and higher quality than Value Noise, but it's of course slighty more // expensive. // // The princpiple is to create a virtual grid/latice all over the plane, and assign one // random vector to every vertex in the grid. When querying/requesting a noise value at // an arbitrary point in the plane, the grid cell in which the query is performed is // determined (line 32), the four vertices of the grid are determined and their random // vectors fetched (lines 37 to 40). Then, the position of the current point under // evaluation relative to each vertex is doted (projected) with that vertex' random // vector, and the result is bilinearly interpolated (lines 37 to 40 again) with a // smooth interpolant (line 33 and 35). // 算法解析:创建一个由若干虚拟晶格组成的平面,接着给每个晶格的顶点赋予一个随机的向量(通过hash函数生成), // 然后通过fract函数将该点平移到【x:0-1, y:0-1】的空间中,再计算到各个晶格顶点的距离向量, // 然后将这两个向量进行dot,最后dot的结果利用ease curves(即u)进行双线性插值。 // 注意:Gradient Noise并不是Value Noise,也不是Perlin Noise,而是基于Perlin Noise的一种分形布朗运动 //(Fractal Brownian Motion,FBM)的叠加 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 hash21(vec2 p) { return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453); //vec3 p3 = fract(vec3(p.xyx) * .1931); //p3 += dot(p3, p3.yzx + 19.19); //return fract((p3.x + p3.y) * p3.z); } // ================================================================================= float noise( in vec2 p ) { vec2 i = floor( p ); vec2 f = fract( p ); // Ease Curve //vec2 u = f*f*(3.0-2.0*f); vec2 u = f*f*f*(6.0*f*f - 15.0*f + 10.0); return mix( mix( dot( hash22( i + vec2(0.0,0.0) ), f - vec2(0.0,0.0) ), dot( hash22( i + vec2(1.0,0.0) ), f - vec2(1.0,0.0) ), u.x), mix( dot( hash22( i + vec2(0.0,1.0) ), f - vec2(0.0,1.0) ), dot( hash22( i + vec2(1.0,1.0) ), f - vec2(1.0,1.0) ), u.x), u.y); //return dot(hash22(i+vec2(0.0, 0.0)), f-vec2(0.0, 0.0)); //return dot(hash22(i+vec2(1.0, 0.0)), f-vec2(1.0, 0.0)); //return mix(dot(hash22(i+vec2(0.0, 0.0)), f-vec2(0.0, 0.0)), // dot(hash22(i+vec2(1.0, 0.0)), f-vec2(1.0, 0.0)), u.x); //return dot(hash22(i+vec2(0.0, 1.0)), f-vec2(0.0, 1.0)); //return dot(hash22(i+vec2(1.0, 1.0)), f-vec2(1.0, 1.0)); //return mix(dot(hash22(i+vec2(0.0, 1.0)), f-vec2(0.0, 1.0)), // dot(hash22(i+vec2(1.0, 1.0)), f-vec2(1.0, 1.0)), u.x); } float noise_fractal(in vec2 p) { p *= 5.0; mat2 m = mat2( 1.6, 1.2, -1.2, 1.6 ); float f = 0.5000*noise(p); p = m*p; f += 0.2500*noise(p); p = m*p; f += 0.1250*noise(p); p = m*p; f += 0.0625*noise(p); p = m*p; return f; } float noise_sum_abs(vec2 p) { float f = 0.0; p = p * 7.0; f += 1.0000 * abs(noise(p)); p = 2.0 * p; f += 0.5000 * abs(noise(p)); p = 2.0 * p; f += 0.2500 * abs(noise(p)); p = 2.0 * p; f += 0.1250 * abs(noise(p)); p = 2.0 * p; f += 0.0625 * abs(noise(p)); p = 2.0 * p; return f; } float value_noise(vec2 p) { p *= 56.0; vec2 pi = floor(p); //vec2 pf = p - pi; vec2 pf = fract(p); vec2 w = pf * pf * (3.0 - 2.0 * pf); // 它把原来的梯度替换成了一个简单的伪随机值,我们也不需要进行点乘操作, // 而直接把晶格顶点处的随机值按权重相加即可。 return mix(mix(hash21(pi + vec2(0.0, 0.0)), hash21(pi + vec2(1.0, 0.0)), w.x), mix(hash21(pi + vec2(0.0, 1.0)), hash21(pi + vec2(1.0, 1.0)), w.x), w.y); } float simplex_noise(vec2 p) { const float K1 = 0.366025404; // (sqrt(3)-1)/2; const float K2 = 0.211324865; // (3-sqrt(3))/6; // 变换到新网格的(0, 0)点 vec2 i = floor(p + (p.x + p.y) * K1); // i - (i.x+i.y)*K2换算到旧网格点 // a:变形前输入点p到该网格点的距离 vec2 a = p - (i - (i.x + i.y) * K2); vec2 o = (a.x < a.y) ? vec2(0.0, 1.0) : vec2(1.0, 0.0); // 新网格(1.0, 0.0)或(0.0, 1.0) // b = p - (i+o - (i.x + i.y + 1.0)*K2); vec2 b = a - o + K2; // 新网格(1.0, 1.0) // c = p - (i+vec2(1.0, 1.0) - (i.x+1.0 + i.y+1.0)*K2); vec2 c = a - 1.0 + 2.0 * K2; // 计算每个顶点的权重向量,r^2 = 0.5 vec3 h = max(0.5 - vec3(dot(a, a), dot(b, b), dot(c, c)), 0.0); // 每个顶点的梯度向量和距离向量的点乘,然后再乘上权重向量 vec3 n = h * h * h * h * vec3(dot(a, hash22(i)), dot(b, hash22(i + o)), dot(c, hash22(i + 1.0))); // 之所以乘上70,是在计算了n每个分量的和的最大值以后得出的,这样才能保证将n各个分量相加以后的结果在[-1, 1]之间 return dot(vec3(70.0, 70.0, 70.0), n); } // ----------------------------------------------- void mainImage( out vec4 fragColor, in vec2 fragCoord ) { vec2 p = fragCoord.xy / iResolution.xy; vec2 uv = p * vec2(iResolution.x/iResolution.y,1.0); float f = 0.0; // 1: perlin noise if( p.x<0.2 ) { f = noise( 16.0 * uv ); } // 2: fractal noise (4 octaves) else if(p.x>=0.2 && p.x<0.4) { f = noise_fractal(uv); } // 3:fractal abs noise else if(p.x>=0.4 && p.x<0.6) { f = noise_sum_abs(uv); } // 4: value noise else if(p.x>=0.6 && p.x<0.8) { f = value_noise(uv); } // 5:simplex_noise else { f = simplex_noise(16.0*uv); } f = 0.5 + 0.5*f; // 分割线:注意如果第三个参数超过了限定范围就不进行插值 f *= smoothstep(0.0, 0.005, abs(p.x-0.2)); f *= smoothstep(0.0, 0.005, abs(p.x-0.4)); f *= smoothstep(0.0, 0.005, abs(p.x-0.6)); f *= smoothstep(0.0, 0.005, abs(p.x-0.8)); fragColor = vec4( f, f, f, 1.0 ); }
注意:
1.fragCoord的取值范围是[vec2(0.0, 0.0), iResolution.xy];
2.Simplex噪声最后归一化所使用的最大值70.0,是当输入点在某一边中点时取得,此时|a| = √2/2, |b| = |c| = 1/√6,得h=(0,1/3,1/3)。取最大值时,梯度和距离向量的点乘结果可以认为是两者模的乘积,而梯度模的最大值为√2,因此最后和的最大值为:
134⋅16√⋅2√⋅2≈170
即 n 中第二分量与第三分量的和。
因此,我们最后把结果乘以70。那么,如果r^2取0.6,最后需要大约乘以为24.51。利用这个想法,我们可以在任意维度下计算最后的伸缩值,例如在三维下,单形,即正四面体的边长为√3/2,当r2取0.5时,最后大概需要乘以31.32。
2.Perlin噪声的应用
最后附上一个据说是基于Perlin噪声的火球效果:GLSL Fragment代码和我总结的注释如下:
const vec2 iResolution = vec2(640.0, 640.0); uniform float iGlobalTime; float snoise(vec3 uv, float res) { // ❤ const vec3 s = vec3(1e0, 1e2, 1e3); //const vec3 s = vec3(1., 100., 1000.); 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 v = vec4(uv0.x, uv0.y, uv1.x, uv1.y); // ❤ 影响形状和速度 vec4 r = fract(sin(v*1e-1)*1e3); //vec4 r = fract(sin(v)); 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); //r = fract(sin(v)); 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 main(void) { // 换算到[(-.5, -.5), (.5, .5)] vec2 p = -.5 + gl_FragCoord.xy / iResolution.xy; // 换算到[(-1., -1.), (1., 1.)] //vec2 p = (2.*gl_FragCoord.xy - iResolution.xy) / iResolution.xy; //p *= 0.5; // 放大2倍 // 根据屏幕纵横比变换 p.x *= iResolution.x/iResolution.y; // 屏幕中心到边界,亮度由高到低 // 定义火焰的基本形状 float color = 3.0 - (3.*length(2.*p)); //float color = 3.0 - (3.*length(2.*p - vec2(-.5, .5))); //float color = 3.0 - (3.*length(2.*p - vec2(2*p.x, 0.0))); // ❤ 控制火焰发散的形式 vec3 coord = vec3(atan(p.x,p.y)/6.2832+.5, length(p)*.4, 0.5); //vec3 coord = vec3(p.y, 0, 0); //vec3 coord = vec3(p.x, 0, 0); //vec3 coord = vec3(atan(p.x,p.y), 0, 0); //vec3 coord = vec3(length(p)*.4, 0, 0); // 控制颜色的层次 for(int i = 1; i <= 7; i++) { float power = pow(2.0, float(i)); color += (1.5 / power) * snoise(coord + vec3(0.,-iGlobalTime*.05, iGlobalTime*.01), power*16.); //snoise(coord + vec3(0., 0.05, 0.01), power*16.); } gl_FragColor = vec4( color, pow(max(color,0.),2.)*0.4, pow(max(color,0.),3.)*0.15 , 1.0); }
注:
z = atan(x, y)/6.2832+.5 ; x, y ∈(-1, 1).
z = length(p)*.4;
z = f*f*(3.0-2.0*f); f∈(0, 1) 相当于GLSL 的 smoothstep
相关文章推荐
- Shader特效——实现“火苗”【GLSL】
- Shader特效——“水彩画”的实现【GLSL】
- Shader特效——“Voronoi 图像”的实现 【GLSL】
- Shader特效——“Simple 3D Raymarch”的实现 【GLSL】
- Shader特效——“帧动画效果”的实现 【GLSL】
- Shader特效——实现简单的“FishEye”【GLSL】
- Shader特效——“Canny边缘检测”的实现 【GLSL】
- Shader特效——“Sephia等效果”的实现 【GLSL】
- Shader特效——“径向模糊”的实现 【GLSL】
- Shader特效——“素描 Pencil Sketch”的实现【GLSL】
- Shader特效——“马赛克”的实现【GLSL】
- Shader特效——"Floyd Steinberg 抖动” 的实现 【OpenCV】【GLSL】
- Shader特效——“Kuwahara Filter”的实现 【OpenCV】【GLSL】
- Shader特效——“Simple RayTrace”的实现 【GLSL】
- Shader特效——实现“Environment Mapping模拟反射和折射”【基于RenderMonkey】
- Shader特效——实现“HDR”效果【GLSL】
- Shader特效——实现“羽化”【GLSL】
- Shader特效——“Bilateral Filter”的实现 【GLSL】
- Shader特效——“Invaders Invaders(火星文雨)效果”的实现 【GLSL】
- Shader特效——“Point in Box”的实现 【GLSL】