欢迎继续一起踏上 The Journey of Chaos, 关于 Shader 生成技术的一些基础性学习。噪声会分为以下几篇内容学习
-
Simplex Noise变种PsrdNoise(本篇)
-
PsrdNoise 应用 FlowNoise
-
CurlNoise
-
FBM深入
其源码和解释可以在 Github github.com/stegu/psrdn… 查看,我也复制了一份,将 shader程序变成 gif便于查看。 可以查看 PsrdNoise原文
PsrdNoise 是一个 Simplex Noise的变种,由瑞典的一名学者Stefan Gustavson 2022年提出。其源码和解释可以在 Github github.com/stegu/psrdn… 查看。毕竟是大学老师,讲的非常详细。 本文是全文通读下来的学习记录,建议大家直接看原文
我看到这个 Noise是看到 shopify 在 24 年底一篇技术播客 shopify.engineering/how-we-buil… 里面, 在制作地球材质中,他使用了 PsrdNoise的 flow noise
的用法达到了符合设计的卡通地球效果,同时性能强大。
PsrdNoise是一系列特性的缩写
-
P: periodic 周期性的
-
S: simplex说明这个算法是基于 simplex noise的
-
R: rotating gradients 算法一个核心点是旋转局部噪声梯度
-
D: derivatives 基本上名字中已经揭示了算法的核心,接下去我们将进行深入理解。
从 Simplex到 Psrd
2503010407
上图是一个psrdNoise生成的图像,我们暂时不考虑 psrdnoise具体算法,其代码实现为
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 uv = (fragCoord * 2.- iResolution.xy)/iResolution.y;
uv *= 5.0;
const vec2 p = vec2(0.0);
float alpha = 0.0;
vec2 g;
float n = 0.5 + 0.5 * psrdnoise(uv, p, alpha, g);
vec3 ncolor = vec3(n);
// Output to screen
fragColor = vec4(ncolor,1.0);
}
这时候会发现他有 4 个参数, 其参数作用如下
参数
作用
示例代码
uv
输入纹理坐标,决定噪声采样位置
uv *= 5.0
p
周期性参数(periodic
),设置噪声的重复周期,支持独立控制 x/y 方向
vec2 p = vec2(6.0, 4.0);
alpha
旋转梯度参数,控制噪声特征的动态旋转(rotating gradients
)
float alpha = time;
g
输出梯度向量,提供噪声场的精确导数,用于动态效果(如流动、折射模拟)
vec2 g; psrdnoise(v, p, alpha, g);
理解 Psrd需要先理解 Simplex, 这个算法我之前已经写过一篇柏林噪声优化 Simplex Noise, 需要理解是 Psrd是基于 Simplex 噪声(单纯形噪声),通过六边形网格(2D 场景)生成更自然的噪声分布。 这里的单纯形网格的点. 从下面这张图可以看出每个网格的点
周期性 periodic
注意上图每个相方块里面的都是相似的,但是不会影响到其不同方块之间的连续性。方块是有第二个参数 P(period)控制的, 如果p.x为 0,表示在 x轴不重复。 其核心代码为下面这一段
// 5), 6) wrap to period and adjust i0, i1, i2 accordingly if(any(greaterThan(period, vec2(0.0)))) { xw = vec3(v0.x, v1.x, v2.x); yw = vec3(v0.y, v1.y, v2.y); if(period.x > 0.0) xw = mod(vec3(v0.x, v1.x, v2.x), period.x); if(period.y > 0.0) yw = mod(vec3(v0.y, v1.y, v2.y), period.y); iu = floor(xw + 0.5*yw + 0.5); iv = floor(yw + 0.5); } else { iu = vec3(i0.x, i1.x, i2.x); iv = vec3(i0.y, i1.y, i2.y); }
这里只是简单的 mod, 但是考虑 shear,所以 iu的需要增加0.5*yw
. 同时为了保障周期不会影响连续性, mod需要增加 0.5.
旋转梯度
还是回到simplex noise, 在2D simplex noise中,晶格是一个等边三角形,晶格的边缘点是梯度的初始值,以晶格边缘点画一个长度为三角形边的圆,这个边缘点的影响范围在这个圆内。
如果我们将每个边缘点的梯度以同样的速度旋转会产生什么效果呢? 这就来到了 Psrd的关键梯度旋转 期代码如下
vec3 psi = hash*0.07482 + alpha; vec3 gx = cos(psi); vec3 gy = sin(psi); vec2 g0 = vec2(gx.x, gy.x); vec2 g1 = vec2(gx.y, gy.y); vec2 g2 = vec2(gx.z, gy.z);
g0,g1,g2表示三个点的梯度,
2503022315
这个时候将限制去掉, 左边是 1 倍速,右边是 4 变速。 在速度越来越快能够比较清楚的看到 grid的晶体结构像一个六边形,所以作者建议要谨慎试用该参数
动画分型
之前我们有说过 fbm, 但是因为有了 alpha参数,我们可以基于 alpha参数做一个分型,会得到非常有趣的效果。 其分型累加的代码如下
float alpha = 0.5*iTime;float n = 0.5;n += 0.4 * psrdnoise(1.0 * uv+0.0, p* 1.0, 1.0 *alpha, g);n += 0.2 * psrdnoise(2.0 * uv+0.1, p* 2.0, 2.0 *alpha, g);n += 0.1 * psrdnoise(3.0 * uv+0.2, p* 4.0, 4.0 *alpha, g);n += 0.05 * psrdnoise(8.0 * uv+0.3, p* 8.0, 8.0 *alpha, g);n += 0.025* psrdnoise(16.0* uv+0.0, p*16.0, 16.0*alpha, g);fragColor = vec4(vec3(n),1.0);
可以看到除了做了分型累加,也做了 uv偏移,还做了旋转角的分型
梯度
最后是参数 Gradient, 其作用是返回噪声函数的梯度。从图中可以看出,梯度是变化最大的方向。如果这一块变化很大,那么梯度的箭头就很长。
还记得这张图吗,这是柏林噪声的 4 个点形成的曲面,换到Simplex控制点只有三个,同时可以通过函数求出点的导数,也就是说可以求出精确梯度 exact derivative,而不是通过多次计算得到的差分近视值。其核心代码如下,
// 10) Compute radial falloff vec3 w = 0.8 - vec3(dot(x0, x0), dot(x1, x1), dot(x2, x2)); w = max(w, 0.0); vec3 w2 = w*w; vec3 w4 = w2*w2;// 11) Linear ramp along gradient (by a scalar product) vec3 gdotx = vec3(dot(g0, x0), dot(g1, x1), dot(g2, x2));// 12), 13) Multiply and sum up noise terms float n = dot(w4, gdotx);// 14) Compute first order partial derivatives vec3 w3 = w2*w; vec3 dw = -8.0*w3*gdotx; vec2 dn0 = w4.x*g0 + dw.x*x0; vec2 dn1 = w4.y*g1 + dw.y*x1; vec2 dn2 = w4.z*g2 + dw.z*x2; gradient = 10.9*(dn0 + dn1 + dn2);
当然我们增加旋转,并且把 scale调整的小一点, 可以看的更加清晰
只此 Psrd所有特性就讲完了,在这里作者通过梯度做了一些简单的颜色配置,就获得非常漂亮的图形,其配置是如果单的 梯度的 x分量>0.5 显示红, y分量>0.5 在叠加颜色,具体代码录下
vec3 gradientcolor = vec3(0.0, 0.5+g*0.11); vec3 bgcolor = vec3(0.0, 0.0, 0.5); vec3 xcolor = vec3(1.0, 0.0, 0.0); vec3 ycolor = vec3(0.0, 1.0, 0.0); vec3 mixcolor = mix(bgcolor, xcolor, aastep(0.6, g.x)); mixcolor = mix(mixcolor, ycolor, aastep(0.6, g.y));
左侧是加了阈值的图形,右侧是通过 green和 blue颜色通道查看梯度, 左侧很漂亮吧,像虫子...
梯度plus
程序纹理不是关于 “什么是正确的”,而是关于 “看起来不错的”,所以不要害怕尝试一些疯狂的想法!, 例如接下去这个例子就是作者完全抛弃物理规律,,做出了水面透射和波纹的效果。
2503024932
这里作者使用了2次随机。为了更好理解,我这里拆出这两个函数
-
使用梯度实现uv的扭曲,从何实现水面下的效果
float n = psrdnoise(uv, p, alpha, g);float disort = dot(g, vec2(1., 4.)) * 0.5 * clamp(-st.t, 0.0 , 1.0);return vec3(disort);
2503024306
-
使用随机值作为水面的上下起伏
float w = clamp(-st.t + 0.01*n, 0.0, 1.0);float mask = aastep(0.01,w); return vec3(mask);
2503024557