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)); 平滑差值
阶梯函数(图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
结果为。
噪声(图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
}
结果为。
渐变噪声(图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
}
结果为。
旋涡效果(图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
}
效果为。
基于距离场的漩涡(图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
结果为。
形状(图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
}
效果如下。
三角优化(图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
}
效果如下。
流体(图9)