VHS with wiggle
ps: don’t actually call
Port of hunterk’s vhs shader for RetroArch. It has parameters for color smear amount, wiggle, wiggle speed and quality of blurring via samples.
If you’re having performance problems lower the blur samples. Do keep in mind that the smearing will look worse, however.
Instructions:
1- Create a CanvasLayer node
2- Add a ColorRect to the CanvasLayer node
3- Click on the ColorRect -> Material -> New ShaderMaterial -> Shader -> Create New Shader -> Paste the code below
CREDITS
- hunterk on the RetroArch GitHub https://github.com/exokitxr/emukit/blob/f3b41ed82eb5e870e3b7d7f6ffdf99f311ec37c2/assets/frontend/bundle/shaders/shaders_glsl/vhs/shaders/vhs.glsl
- Original author on ShaderToy https://www.shadertoy.com/view/XlsczN
- GDQuest for the project demo (second screenshot): https://www.gdquest.com/news/2022/12/godot-4-third-person-controller/
Shader code
shader_type canvas_item;
group_uniforms Wiggle;
uniform float wiggle : hint_range(0.0, 1.5, 0.01) = 0.03;
uniform float wiggle_speed : hint_range(0.0, 100.0, 1.0) = 25;
group_uniforms Smear;
uniform float smear : hint_range(0.0, 2.0) = 1.0;
uniform sampler2D Source : hint_screen_texture, filter_linear_mipmap, repeat_disable;
group_uniforms Blur;
uniform int blur_samples : hint_range(3, 15, 1) = 15;
float onOff(float a, float b, float c, float framecount) {
return step(c, sin((framecount * 0.001) + a * cos((framecount * 0.001) * b)));
}
vec2 jumpy(vec2 uv, float framecount) {
vec2 look = uv;
float window = 1.0 / (1.0 + 80.0 * (look.y - mod(framecount / 4.0, 1.0)) * (look.y - mod(framecount / 4.0, 1.0)));
look.x += 0.05 * sin(look.y * 10.0 + framecount) / 20.0 * onOff(4.0, 4.0, 0.3, framecount) * (0.5 + cos(framecount * 20.0)) * window;
float vShift = (0.1 * wiggle) * 0.4 * onOff(2.0, 3.0, 0.9, framecount) * (sin(framecount) * sin(framecount * 20.0) + (0.5 + 0.1 * sin(framecount * 200.0) * cos(framecount)));
look.y = mod(look.y - 0.01 * vShift, 1.0);
return look;
}
vec2 Circle(float Start, float Points, float Point) {
float Rad = (3.141592 * 2.0 * (1.0 / Points)) * (Point + Start);
return vec2(-(.3 + Rad), cos(Rad));
}
vec3 rgb2yiq(vec3 c) {
return vec3(
(0.2989 * c.x + 0.5959 * c.y + 0.2115 * c.z),
(0.5870 * c.x - 0.2744 * c.y - 0.5229 * c.z),
(0.1140 * c.x - 0.3216 * c.y + 0.3114 * c.z)
);
}
vec3 yiq2rgb(vec3 c) {
return vec3(
(1.0 * c.x + 1.0 * c.y + 1.0 * c.z),
(0.956 * c.x - 0.2720 * c.y - 1.1060 * c.z),
(0.6210 * c.x - 0.6474 * c.y + 1.7046 * c.z)
);
}
vec3 Blur(vec2 uv, float d, int samples) {
vec3 sum = vec3(0.0);
float W = 1.0 / float(samples);
for (int i = 0; i < samples; ++i) {
float t = (sin(TIME * 5.0 + uv.y * 5.0)) / 10.0;
t = 0.0;
vec2 PixelOffset = vec2(d + 0.0005 * t, 0);
float Start = 2.0 / float(samples);
vec2 Scale = 0.66 * 4.0 * 2.0 * PixelOffset.xy;
vec3 N = texture(Source, uv + Circle(Start, float(samples), float(i)) * Scale).rgb;
sum += N * W;
}
return sum;
}
void fragment() {
vec2 uv = UV;
float d=0.1-round(mod(TIME/3.0,1.0))*.1;;
uv = jumpy(uv, mod(TIME * wiggle_speed, 7.0));
float s = 0.0001 * -d + 0.0001 * wiggle *(sin(TIME * wiggle_speed));
float e = min(.30,pow(max(0.0,cos(uv.y*4.0+.3)-.75)*(s+0.5)*1.0,3.0))*25.0;
float r = (TIME*(2.0*s));
uv.x += abs(r*pow(min(.003,(-uv.y+(.01*mod(TIME, 5.0))))*3.0,2.0)) * wiggle;
d = 0.051+abs(sin(s/4.0));
float c = max(0.0001,.002*d) * smear;
vec4 final;
final.rgb = Blur(uv, c + c * uv.x, blur_samples);
float y = rgb2yiq(final.rgb).r;
uv.x += 0.01 * d;
c *= 6.0;
final.rgb = Blur(uv, c, blur_samples);
float i = rgb2yiq(final.rgb).g;
uv.x += 0.005 * d;
c *= 2.50;
final.rgb = Blur(uv, c, blur_samples);
float q = rgb2yiq(final.rgb).b;
final.rgb = yiq2rgb(vec3(y, i, q)) - pow(s + e * 2.0, 3.0);
final.a = 1.0;
COLOR = final;
}
This is cool and all but could we maybe see a posterize time/frame stutter shader/script? Like a shader/script that caps the project to a set fps?
I’m pretty sure you can cap the framerate in the Project Settings, is that what you mean?
thanks for this!
i was getting a white flashing until I commented out the pow on line 93 to
final.rgb = yiq2rgb(vec3(y, i, q));// - pow(s + e * 2.0, 3.0);
(rendering method Mobile on v4.3.dev5.official [c9c17d6ca])
This is exactly what I’ve been looking for. Thanks a lot.
for some reason, after applying it, I can’t control the camera, although I can still control the character
set the mouse mode of the color rect to ignore
nvm i got it