背景
除了生成各种奇形怪状与自然景观,噪声也有其他美妙的用途!
工作原因,经常接触与噪声相关的画面效果(火焰啊,画面扰动啊之类的),做的时候一知半解,傻傻分不清楚各种形态的noise,也不知道三维noise和二维noise的具体区别,只知道在shadertoy上找到相似的噪声然后ctrl c/v一下,特别是在具体的业务场景中,比如设计需要这种形态的火焰,需要那种形态的画面扰动,对我来说简直是挠破了脑袋,噪声调半天都不一定能调出一个满意的结果😂
刚好近期的工作需要分析ae的效果控件【分形杂色】【湍流杂色】,找到一篇原作者对【分形杂色】效果原理的说明,再次引起了对噪声的兴趣,也更希望能啃啃这块软骨!
因此有了这篇文档,是我对各种噪声的学习理解,(有时间的话)下篇再来展示下各种噪声的(画面上的)应用,名字我都想好了!就叫【猴子都会使用的噪声(noise)专题】🤣
前提
接下来的所有代码都能在shadertoy上运行,下面的图像也基本都生成于shadertoy
参考的各种资料文档,也会在相应地方提供出处,但不用阅读出处的具体细节,也能理解本篇的内容!(自信)
由于还是有些内容没理解明白,所以会有一些问题会以斜体引用的形式出现在文档中,用来质问愚蠢的自己,希望自己未来能解决掉提出的问题吧,也希望如果有好哥哥知道答案,能在评论区中留下你的箴言,赞美太阳☀!
(例子)呐,莱纳,为什么妈妈会被巨人吃掉呢?
Hash/White Noise(杂乱/白噪声)
在shadertoy中涉及噪声相关的效果,都不难发现有hash函数(有时候是random函数),再参考一下wiki对white noise的描述(实际上white noise的定义会更加广泛,不局限于图像中),也跟hash函数产生的图像极其相似,所以这里把hash跟white放在一块
不太理解这函数为啥叫hash,而不是white,还是我理解错了hash跟white不能划等号
对于图像来说,每个像素都有其位置,即uv,把uv作为hash函数的输入,可以得到伪随机的结果
这里hash函数的实现挺自由的,各种magic number,而且用到dot,fract,sin等函数或是这些函数的组合,虽然出来的结果大同小异,但还是有些区别的,特别是利用sin作为hash的实现,在放大uv的时候会有规律性的artifact
参考上文的实践,建议还是用dot/fract作为hash的实现,如果用带有sin的hash函数,接下来的噪声实现在uv放大后都有周期性的表现,而且在不同的机子中(比如安卓/ios)由于高频下精度不同会导致sin函数输出的结果有差异,可能会有不同设备输出结果不一致的问题
float hash(vec2 p) {
vec3 p3 = fract(vec3(p.xyx) * .1031); p3 += dot(p3, p3.yzx + 33.33); return fract((p3.x + p3.y) * p3.z); }
float hash_with_sin(vec2 _st) {
return fract(sin(dot(_st.xy, vec2(12.9898,78.233)))*43758.5453123); }
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 uv = fragCoord/iResolution.xy;
float i = floor(uv.x);
float scale = 1000.; // 尝试放大这个值,会发现hash_with_sin与hash的区别
vec3 col = vec3(random(uv*scale + iTime*0.001))*step(.5, uv.x);//RIGHT
vec3 colsin = vec3(randomSin(uv*scale + iTime*0.001))*(1.-step(.5, uv.x));//LEFT
vec3 line = vec3(smoothstep(.5, .5, uv.x)) - vec3(smoothstep(.51, .51, uv.x));
// Output to screen
fragColor = vec4(col + colsin + line,1.0);
}
左hash_with_sin,右hash,就算是观察静止的图片也能看到左边呈现一些规律性,如果加上iTime会更加明显
参考另外一个链接的hash库,无敌了:【shader】超级噪声库,附代码(fbm、Perlin、Simplex、Worley、Tiling、Curl等,很全很全)
hash库,不用纠结具体怎么实现,里面的magic number也不需要额外关心,随意就行
float hash11(float p) {
p = fract(p * .1031); p *= p + 33.33; p *= p + p; return fract(p); }
float hash21(vec2 p) {
vec3 p3 = fract(vec3(p.xyx) * .1031); p3 += dot(p3, p3.yzx + 33.33); return fract((p3.x + p3.y) * p3.z); }
float hash31(vec3 p3) {
p3 = fract(p3 * .1031); p3 += dot(p3, p3.zyx + 31.32); return fract((p3.x + p3.y) * p3.z); }
vec2 hash12(float p) {
vec3 p3 = fract(vec3(p) * vec3(.1031, .1030, .0973));