共计 2602 个字符,预计需要花费 7 分钟才能阅读完成。
随同着赛博朋克 2077 的火爆(各种意义上的),这种电子故障类的 shader 仿佛成为了一种时尚,因为你真不知道这是 bug 还是无意为之。明天咱们就来了了几款故障类 shader 的原理及实现吧!本期将介绍 2 种 shader(色彩偏移与扫描偏移)下期咱们再说两种(抖动和摇摆),正当应用这四种 shader 你可能实现上面短片中的成果:
色彩偏移
最终成果
咱们都晓得,图片是由 RGB 三个通道的色彩叠加起来而成的。R+ G 失去黄,R+ B 失去洋红,G+ B 是青。
色彩偏移的原理就是把其中一个通道偏移一个间隔在叠加起来。比方挪动绿色通道:
void main () {
// Color drift
float drift = sin (u_time) * u_colorDrift * .04;
// Offset
vec2 offset = vec2(drift,.0);
vec4 color1 = texture2D (u_image0, uv);
vec4 color2 = texture2D (u_image0, uv + offset);
gl_FragColor = vec4 (color1.r, color2.g, color1.b, 1.0);
}
其中 u_time 是以后工夫,保障继续抖动,u_colorDrift 是横向偏移的间隔:
是不是 you 点抖音的感觉?那么为什么同种呈现的色彩是洋红和绿??那是因为咱们挪动了绿通道,而洋红就是剩下的蓝通道和红通道叠加的后果。这里咱们能够试验一下,如果挪动红通道。失去的抖动色彩就会是红色和青色:
gl_FragColor =vec4(color2.r, color1.g, color1.b,1.0);
扫描射线
这种成果会在产生逐条的偏移,相似小时候电视机调台时那种感觉(裸露年龄了)
这里咱们须要一个随机函数对不同 y 进行不同的偏移(图中能够看出,对 y 方向雷同的点偏移是统一的)。这里咱们须要一个 hash 函数:
float hash21 (float x, float y) {return fract (sin (dot (vec2 (x, y), vec2 (12.9898, 78.233))) * 43758.5453);
}
核心思想是通过 fract(sin(x)*a)当 a 是一个很大的数时结构出出一种随机:
这种理念在须要生成躁点的 shader 中非常常见,再配合 smoothstep,step,u_time 能够写出很多难以置信的成果,当前咱们会常常遇到。
此时在 main 函数中咱们曾经能够实现代码逻辑了:
void main(){
float y = uv.y;
float jitter = hash21 (y, .0) * 2. - 1.;
vec2 offset = fract (vec2 (uv.x + jitter, .0));
vec4 color = texture2D (u_image0, offset);
gl_FragColor = color;
}
offset 加上 fract 是保障偏可能间断:
图中上为没有加 fract 的成果,下为加了 fract 的成果,咱们应用扫描偏移时,因为不至于偏移到半个屏幕,所以不加 fract 成果也未必很显著,但前面说的两种属性(抖动和摇摆)是很容易挪动半个屏幕的,所以必须加上 fract。
最初因为咱们须要通过参数管制扫描射线成果,所以咱们还须要加上一个阀门函数。
jitter *= step (u_scan,abs (jitter)) ;
其中 step 函数, 用于尖利的适度,y = step(a,x)当 x 大于 a 时 y =1;否则等于 0:
a 等于 0.5 时
调换 x 与 a 的程序相同:
咱们来剖析一下 step (u_scan, abs (jitter)); 因为 abs (jitter)是一个值域介于 0 -1,当 u_scan 趋近于 0 时,step (u_scan, abs (jitter))就更大概率等于 1;当 u_scan 趋近于 1 时,step (u_scan, abs (jitter))就更大概率等于 0;
所以通过管制 u_scan 的值就能管制,jitter *= step (u_scan,abs (jitter)) 输入随机数的浓密水平:
u_scan 别离取 0,0.5,0.9 时函数的浓密水平(取 1 就齐全归零了,有趣味的小伙伴能够试试)。然而咱们实习完 u_scan 在 0 的时候最小,1 的时候最大。这个成果正好与咱们所冀望的相同。所以咱们还须要再增加一个函数
float scan = clamp (1.0 - u_scan * 1.2, 0., 1.);
jitter *= step (scan, abs (jitter));
其实这里用 float scan = 1.0 – u_scan 也是能够的。clamp 函数只是保障输入在 [0,1] 之间。
最初咱们把下面两个成果合并:
precision mediump float;
/*
变量申明
*/
varying vec2 uv;
uniform sampler2D u_image0;
uniform float u_scan;
uniform float u_colorDrift;
uniform float u_time;
/*
工具函数
*/
float hash21 (float x, float y) {return fract (sin (dot (vec2 (x, y), vec2 (12.9898, 78.233))) * 43758.5453);
}
void main () {
float x = uv.x;
float y = uv.y;
// Scan line jitter
float jitter = hash21 (y, u_time) * 2. - 1.;
float scan = clamp (1.0 - u_scan * 1.2, 0., 1.);
jitter *= step (u_scanY, abs (jitter)) ;
// Color drift
float drift = sin (u_time * 666. + jump) * u_colorDrift * .04;
// Offset
vec2 offset1 = fract (vec2 (uv.x + jitter , 0.));
vec2 offset2 = fract (vec2 (uv.x + jitter + drift , 0.));
vec4 color1 = texture2D (u_image0, offset1);
vec4 color2 = texture2D (u_image0, offset2);
gl_FragColor = vec4 (color1.r, color2.g, color1.b, 1.0);
}
下期咱们来说说横向和纵向的抖动和摇摆吧。
相干文章:
热成像
像素风
如果你对 shader 感兴趣,也能够看看上面的文章: