伴随着赛博朋克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感兴趣,也可以看看下面的文章:
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。