
上图是本篇实验的实践结果,具体做法需要结合Raymarching部分进行讲解,完整代码后续在Shader实验室: RayMarching(二)中进行详细分析。我们对Noise的拆解还是按照从1D到3D的步骤进行描述。
1D Value Noise
在开篇部分我们说过Value Noise与White Noise的区别是像素点的过渡是否平滑,而要实现平滑过渡很直观的想法是通过线性插值来进行处理,而线性插值的前提是:必须是两个不同的值才可实现插值。根据推论,我们可取floor(x)与floor(x)+1.0作为数据源,再结合随机与噪声:White Noise篇中推出的随机算法进行计算,形如:
float random(float x) { return fract(sin(x * 457.87) * 10000.0); } y1 = floor(x); y2 = floor(x)+1.0; // 以下为伪代码,?号为未知数值, 代表如何插值还未知 float res = mix(random(y1), random(y2), ?); 上述代码如何进行插值是我们接下来要讨论的重点,我们可结合mix函数的定义来分析(等同于Cg中的lerp函数)。
// 当w为0时,返回a,当w为1时返回b,否则返回a与b的插值 float mix(float a, float b, float w) { return a + w*(b-a); }
根据mix的定义可知,mix函数的第三个参数最好是一个0~1间的数,分析到这,我们可以很清晰的想到可以使用fract(x)作为mix函数的第三个参数,原因可看Shader实验室:fract函数。我们把fract(x)代入:
float res = mix(random(y1), random(y2), fract(x));
效果如下:

生成结果发现线与线间的连接处很生硬,如何让其更平滑?或者我们分析过的函数中,哪个函数可以做到平滑?是的,smoothstep函数,那就上吧:
float res = mix(random(y1), random(y2), smoothstep(0., 1., fract(x)));

好了,掌握了1D Value Noise的生成,一起分析下2D Value Noise吧。
2D Value Noise
2D Value Noise与1D Value Noise的原理类似,区别在于2D Value Noise在平面上做插值,如图:

在代码实现上,可通过下图所示方式:

示意代码如下:
vec2 f = floor(vec2(x, y)); vec2 i = fract(vec2(x, y)); float ceil1 = random(f); float ceil2 = random(f+vec2(1,0)); float ceil3 = random(f+vec2(0,1)); float ceil4 = random(f+vec2(1,1)); float ceil12 = mix(ceil1, ceil2, smoothstep(0, 1, i.x)); float ceil34 = mix(ceil3, ceil4, smoothstep(0, 1, i.x)); float result = mix(ceil12, ceil34, i.y);
2D Value Noise的输出结果如下:

3D Value Noise
3D Value Noise相比2D Value Noise多了一个维度,但是不管是1D Value Noise 还是 2D Value Noise,还是我们现在要分析的3D Value Noise原理都是一样的,3D Value Noise的原理如下:

上图是3D立方体,其中绿色线框与蓝色线框分别为立方体的前面与背面,绿色线框与蓝色线框部分的Value Noise值可通过2D Value Noise的插值方式求取,最后通过mix( cell(green), cell(blue), smoothstep(0, 1, fract(z)))求得;示例代码如下:
float valueNoise3(vec3 v) { vec3 f = floor(v); vec3 r = smoothstep(vec3(0.), vec3(1.), fract(v)); return mix( mix(mix(random(f), random(f+vec3(1.,0.,0.)), r.x), mix(random(f + vec3(0.,1.,0.)), random(f + vec3(1.,1.,0.)), r.x), r.y), mix(mix(random(f+vec3(0.,0.,1.)), random(f+vec3(1.,0.,1.)), r.x), mix(random(f + vec3(0.,1.,1.)), random(f + vec3(1.,1.,1.)), r.x), r.y), r.z ); }
Value Noise部分就到这里啦,感兴趣的伙伴可自己实现开篇的案例,由于Value Noise实现的效果会有块状的效果产生(可查看2D Value Noise的输出图片),这也是Perlin Noise提出的原因。
感兴趣的伙伴可关注下知乎Shader实验室,或扫描下方二维码关注我们吧^_^。

本文深入解析了1D、2D及3D ValueNoise的生成原理与实现代码,从线性插值到smoothstep函数的应用,逐步展示了如何创建平滑过渡的噪声纹理。

被折叠的 条评论
为什么被折叠?



