常见噪声函数原理及故障艺术应用
引言: 常常精妙于大自然的鬼斧神功,每个生命都独一无二,也基本无法找到实现其一模一样效果的函数,即使是形如噪声函数这样,能做出类似大理石、云朵一样的效果,但也无法精确描绘每个真实存在的云的形状。
噪声函数,也被称为随机函数或随机场,是一种创建复杂的随机模式的方法,可以用于纹理生成、景观模拟、云朵生成、湖面水波纹生成。具有一定的随机性,但也不是完全无规则,是一种伪随机形态函数。
噪声函数常见的生成步骤有
- 划分网格
- 顶点处生成随机值(或者为标量、或者为矢量)
- 根据周围几个顶点值通过插值计算混合值,得到当前位置的值
在这三个步骤中,如何划分网格、顶点如何生成随机值、插值计算算法、网格边缘处计算方式,这几个问题,存在不同的具体方案。因此,也细分多种噪声函数。
噪声种类
-
值噪声(value noise)
实现简单、能快速生成噪声。
可以用值噪声进行入门,其步骤为
(1)简单的划分网格可以通过
uv * vec2(widthScale, heightScale)
实现(2)找到当前的像素点所在的网格
floor(uv)
(3)通过左上、左下、右上、右下的网格顶点值(注:需要补充图TODO: ),计算对应的随机值
(3)通过插值函数,进行平滑过渡
原理:划分网格后,在每个整数点生成随机标量值,然后使用插值方法在这些点之间创建平滑的过渡。
// 随机函数得到随机值 float 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(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453123); } // value noise函数 float noise(vec2 st) { vec2 i = floor(st); vec2 f = fract(st); vec2 u = f*f * (3.0 - 2.0 * f); // cubic函数,平滑过渡 return mix(mix(random2(i + vec2(0.0, 0.0)), random2(i + vec2(1.0, 0.0)), u.x), mix(random2(i + vec2(0.0, 1.0)), random2(i + vec2(1.0, 1.0)), u.x), u.y); }
效果
详细代码可见: https://github.com/ZhuangWeiMian/cocos_effect/tree/main/assets/noise/value_noise
-
梯度噪声(gradient noise)
在value noise的场景中,由于网格顶点生成随机值的部分,是标量的形式进行生成,没有方向性,因此会出现比较明显的网格状效果。在梯度噪声中,生成的随机值,是以梯度向量的方式生成,缓解了一部分的网格状效果。关键代码部分:
// 生成具有x、y方向的二维随机值 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); }
效果
-
柏林噪声(Perlin noise)(注解上原理文章)
柏林噪声则是一种改进的梯度噪声,在原有基础上,增加了一些平滑过渡插值、八度音阶(叠加多个不同幅度、不同频率),增加了一定的复杂度,但是提供了更高维度的变换效果,更加自然、连续的噪声效果。
效果
完整代码可见https://github.com/ZhuangWeiMian/cocos_effect/tree/main/assets/noise/perlin_noise
-
简单x噪声(Simplex noise)(注解上原理文章)
Simplex噪声则是在柏林噪声上更进一步优化, Simplex 噪声使用的是单纯形网格(在二维空间是等边三角形,在三维空间是四面体),单纯形的所有边的长度都是相等的,因此在任何方向上的距离都是相同的,不会出现像 Perlin 噪声 那样在对角线方向上的距离更远的问题。这就使得 Simplex噪声 在各个方向上的性质更为一致,各向异性更低。
效果
完整代码可见https://github.com/ZhuangWeiMian/cocos_effect/tree/main/assets/noise/simplex_noise
-
细胞噪声(Worley noise)
Worley 噪声基于随机点的分布来生成噪声图案,它具有更自然、更有结构的特性。(因为看起来很像细胞,不喜欢所以不实现。
常见故障艺术
故障艺术(Glitch Art)是赛博朋克风格中常见的艺术风格,通过分离变化图像中的颜色,进一步艺术加工,呈现不一样的风格。
常见的故障艺术类型,有四类,主要分为颜色分离、异常色块、uv纹理抖动、屏幕抖动。而在这几种类别中,又可以根据变换的图形差异,有新的小类展示
-
颜色分离
又称RGB分离,也称颜色偏离故障。一般会用不同的uv偏移值取色,一般是一个通道的uv不偏移,对另外两个通道uv进行偏移
关键代码:
// 分离参数,复杂可以根据时间变化、频率、幅度 float splitAmout = 0.05; colorR = texture2D(texture, vec2(v_uv0.x + splitAmout, v_uv0.y)); colorG = texture2D(texture, vec2(v_uv0.x, v_uv0.y)); colorB = texture2D(texture, vec2(v_uv0.x - splitAmout, v_uv0.y)); vec3 colorFinal = mix(vec3(colorR.r, colorG.g, colorB.b), colorG.rgb, .2);
原图
效果
复杂一点可以根据时间、叠加多个正弦函数、调整频率
float splitAmout = (1.0 + sin(cc_time.x * 6.0)) * 0.5; splitAmout *= 1.0 + sin(cc_time.x * 16.0) * 0.5; splitAmout *= 1.0 + sin(cc_time.x * 19.0) * 0.5; splitAmout *= 1.0 + sin(cc_time.x * 27.0) * 0.5; splitAmout = pow(splitAmout, 10.0); splitAmout *= (0.05 * 10.0); colorR = texture2D(texture, vec2(v_uv0.x + splitAmout, v_uv0.y)); colorG = texture2D(texture, vec2(v_uv0.x, v_uv0.y)); colorB = texture2D(texture, vec2(v_uv0.x - splitAmout, v_uv0.y)); vec3 colorFinal = mix(vec3(colorR.r, colorG.g, colorB.b), colorG.rgb, .2);
效果
完整代码:https://github.com/ZhuangWeiMian/cocos_effect/tree/main/assets/glitch_art/rgb_split -
异常色块
这里主要是色块的生成,形如方块形状、长条形状则可以用
floor
函数生成,floor(uv.y * 10)
这种可以把y轴划分为十份,然后色块随机设置透明度或者色值叠加float randomNoise(vec2 seed) { return mod(sin(dot(seed * floor(cc_time.x * 10.0), vec2(17.13, 3.71))) * 43758.5453123, 1.0); } // 生成随机色块 float noise = randomNoise(floor(st)); // 加强强度 float displaceNoise = pow(noise, 8.0) * pow(noise, 8.0); vec4 colorR = texture2D(texture, vec2(v_uv0.x + displaceNoise * 0.05 * randomNoise(7.0), v_uv0.y)); vec4 colorG = texture2D(texture, vec2(v_uv0.x, v_uv0.y)); vec4 colorB = texture2D(texture, vec2(v_uv0.x - displaceNoise * 0.05 * randomNoise(7.0), v_uv0.y)); gl_FragColor = vec4(colorR.r, colorG.g, colorB.b, 1.0);
进一步,可以调整方块的大小,方块大小不一致,调整方块形状,比如圆形或者其他形状、或者是条形状。
// 生成随机色块 叠加不同形状的方形 float noise1 = randomNoise(floor(st.xy * 5.0)); float noise2 = randomNoise(floor(st.xy * 3.0)); // 加强强度 float displaceNoise1 = pow(noise1, 2.0); float displaceNoise2 = pow(noise2, 6.0); float displaceNoise = fract(displaceNoise1 * displaceNoise2 * 10.0);
效果
-
uv分层抖动
在生成色块后,移动uv,使得图像呈现抖动效果,核心在于uv的变化
vec2 st = v_uv0.xy * 1.0; float strength = .1; float splitNumber = 10.0; float pixelSizeX = .5; float speed = 50.0; // 划分变化区域 通过调整uv采样的位置 模拟了屏幕信号的块状抖动传输故障 if (mod(st.y * splitNumber, 2.0) < 1.0) { st.x += pixelSizeX * cos(cc_time.x * speed) * strength; } vec4 colorG = texture2D(texture, vec2(st.x, st.y)); gl_FragColor = vec4(colorG.r, colorG.g, colorG.b, 1.0);
效果
完整代码:https://github.com/ZhuangWeiMian/cocos_effect/tree/main/assets/glitch_art/tile_jitter进一步,可以变化色块的大小,实现差异的色块大小
-
扫描线&噪点故障
实现上主要是通过对uv进行基于noise的抖动效果
float jitter = randomNoise(st.y, cc_time.x) * 2.0 - 1.0; jitter *= step(scanLineJitterY, abs(jitter)) * clamp(sin(cc_time.x / 2.0) + sin(cc_time.x) + sin(cc_time.x / 4.0), 0.0, 1.0); vec4 colorG = texture2D(texture, mod(st + vec2(jitter, 0.0), 1.0)); gl_FragColor = colorG;
效果
完整代码: https://github.com/ZhuangWeiMian/cocos_effect/tree/main/assets/glitch_art/scan_line_jitter模拟噪点故障,在于用noise去扰动原先场景图的颜色值
float speed = 2.0; vec4 colorG = texture2D(texture, st); float noiseX = randomNoise(cc_time.x * speed + st / vec2(-213, 5.53)); float noiseY = randomNoise(cc_time.x * speed - st / vec2(213, - 5.53)); float noiseZ = randomNoise(cc_time.x * speed + st / vec2(213, 5.53)); colorG.rgb += 0.5 * vec3(noiseX, noiseY, noiseZ) - 0.25; gl_FragColor = vec4(colorG.r, colorG.g, colorG.b, 1.0);
效果
完整代码: https://github.com/ZhuangWeiMian/cocos_effect/tree/main/assets/glitch_art/analog_noise
除了类似的故障艺术之外,同时也会有一些特殊类似图片消融、湖泊、水面、树叶、云朵的生成。同时在一些需要随机的场景中也能有比较好的表现。