数字孪生噪声Shader中生成世界13 Pseudo Turbulence 伪湍流

279 阅读5分钟

欢迎继续一起踏上 The Journey of Chaos, 关于 Shader 生成技术的一些基础性学习。噪声会分为以下几篇内容学习

  1. 随机函数与白噪声
  2. 值噪声 ValueNoise
  3. 柏林噪声 Gradient Noise
  4. 多维柏林噪声
  5. 柏林噪声优化 Simplex Noise
  6. Cell Noise
  7. SmoothVoronoi 与VoroNoise
  8. Simplex Noise变种PsrdNoise
  9. PsrdNoise 应用 FlowNoise
  10. CurlNoise 与 Streamline Shaders
  11. CurlNoise调节与边界
  12. 2D湍流 (本篇)
  13. FBM深入

最近 Twitter@Xordev 在 shadertoy中火力全开,他在 2025.03.18 发表了一篇关于 湍流 生成技术的文章 mini.gmshaders.com/p/turbulenc… 。 然后基于此技术做出了一堆让人哇塞的shader, 比如 下面的幽灵和天使两个 shader weixin.qq.com/sph/AAL11I9…

weixin.qq.com/sph/AIvYK0r…

他的代码我完全看不懂,都非常短,也就是 20 行。但我觉得其核心就是他这第一篇文章,以下内容是我的学习的一些记录和对相关内容的一个补充理解。另外内容中还有大量的 psrdnoise的作者(60 岁的大叔)三天前出的一本新书《Noise is Beautiful》。 这位大叔认为自己的这本书填补了 程序化生成技术几十年来的空白。 因为我还刚入门,所以不清楚说的对不对。

地火水气都是湍流

湍流/漩涡 是流体流动中出现的一种复杂且视觉有趣的现象,本质上是一种混动效应。 当湍流被看到的时候其实已经发生了很久。例如我们可以看到水的波纹,而实际上风吹动的水波从小小涟漪开始,以自下而上的方式继续能量, 小涟漪随着时间的推移组合成大浪。另一方面,湍流将能量转移到另一个方向:向一个方向旋转的大漩涡与不移动的介质部分相遇,这耗尽了它们的动能,并产生了较小的漩涡向相反的方向旋转。这些较小的漩涡反过来又产生更小的漩涡,这个过程一直持续到由于流体的粘力使最小的漩涡消散为热量。对于水中的湍流,这种情况发生在厘米级。空气中的湍流粘度比水低得多,可以持续到亚毫米级。

Bigger whirls have little whirls that feed on their velocity, and little whirls have lesser whirls, and so on to viscosity”

实际上,湍流是我们周围经典“四要素”的大部分视觉细节的原因:水、气、火和土。湍流既发生在水的内部,也发生在水的表面,大多数水波都是由湍流引起的。 火也是一种湍流现象,燃烧和热膨胀会产生大量的内部运动。空气几乎在任何地方都有自然湍流,包括缓慢的速度和巨大的规模。 巨大的规模产生的现象就是为“天气”。 而土壤、沙子、岩石和地面的一般含义中,是由空气和水的侵蚀形成的,而土与空气/水的相互作用就是湍流的形成过程

水和空气等透明介质中的湍流并不总是直接可见的,但它可能会产生相当惊人的次要影响。下图是一些例子。烟雾或水漩涡在几秒钟内产生,天气模式需要几天才能形成,岩石的侵蚀需要数百万年,但它们都是由湍流引起的。

2504273701

湍流难解

尽然 火,水,尘,风,雾,烟,云这些都深受湍流影响,那么有没有什么数学方法描述这个现象,然后通过解方程的方法完成模拟呢? 答案是有的!而且价值 一百万美金。 这个方程是 navier-stokes 方程 2504274200

有兴趣获取奖金的,可以访问这个Guardian 链接 www.theguardian.com/science/blo…

正因为湍流计算的复杂性 Xor准备 Fake it。 他提出了一种独创性的方法,能够一定程度模拟湍流,计算代价非常小

波,波,波

总的来说,xor做的事情就是通过 sin函数增加了需要多坐标的变形。 同时在 分型的过程中,增加旋转以去除网格的秩序感。

从一个波开始

pos.x += WAVE_AMP * sin(pos.y * freq) / freq;

这个 WAVE_AMP可以是[0, 1]的一个值,不要超过 1. 你知道的。。。 超过 1 shader就不好处理了。 同时在不同的分型层级对坐标增加一个旋转角度, 具体角度不重要,只要能够打破 方格的秩序感就可以了

//Scroll along the rotated y coordinate
float phase = freq * (pos * rot).y + WAVE_SPEED * u_time;

//Add a perpendicular sine wave offset
pos += WAVE_AMP * rot[0] * sin(phase) / freq;

所有都融合在一起,便有了 turbulence function

//Number of turbulence waves
#define TURB_NUM 10.0
//Turbulence wave amplitude
#define TURB_AMP 0.7
//Turbulence wave speed
#define TURB_SPEED 0.3
//Turbulence frequency
#define TURB_FREQ 2.0
//Turbulence frequency multiplier
#define TURB_EXP 1.4


//Turbulence starting scale
float freq = TURB_FREQ;

//Turbulence rotation matrix
mat2 rot = mat2(0.6, -0.8, 0.8, 0.6);

//Loop through turbulence octaves
for(float i=0.0; i<TURB_NUM; i++)
{
    //Scroll along the rotated y coordinate
    float phase = freq * (pos * rot).y + TURB_SPEED*iTime + i;
    //Add a perpendicular sine wave offset
    pos += TURB_AMP * rot[0] * sin(phase) / freq;

    //Rotate for the next octave
    rot *= mat2(0.6, -0.8, 0.8, 0.6);
    //Scale down for the next octave
    freq *= TURB_EXP;
}

直接看代码难以已连接,为原始 uv配制了一个 grid。 通过观察 grid的扭曲来感知图像的扭曲

2505010654 2505010735
改变迭代次数TURB_NUM改变振幅TURB_AMP

资料

  1. developer.nvidia.com/gpugems/gpu…
  2. mini.gmshaders.com/p/turbulenc…
  3. github.com/stegu/noise…
  4. www.bilibili.com/video/BV1EZ…
  5. www.youtube.com/watch?v=qtI…
  6. www.bilibili.com/video/BV1yy…
  7. www.shadertoy.com/view/tlfGRN