Screen-Space Edge Detection Outline Shader
This shader must be applied to a Quad (MeshInstance) with a size of (2, 2)!
Extra Cull Margin on the quad should be turned up all the way!
This shader gets the depth information for the current pixel and the pixels around it, then instead of just comparing the difference between the depths (this would only give you outlines on the outside of objects, it’s how I did outline_mode 1) It first finds the difference between the center pixel and those around it, then compares the differences to the differences from the opposite side (this easily detects drastic changes in the angle of the depth, ie. edges)
You can change the color of the outline in the Shader Params.
You actually can’t change the width of the outline in this shader though, widening the outline would require lots more computation.
If you’d like to support my work, consider donating some crypto!
bitcoin: bc1qdsmmjp3az6dvr5u00ffxflzuth87zsv7ld4xcg
ethereum: 0x5748889bE74F3012543d82160Fa3E6fBe55a3bff
dogecoin: DSbSHKkwsLE35Bos2vpwxwthXGa9EoP8rB
Shader code
//THIS SHADER MUST BE APPLIED TO A QUAD (MeshInstance) WITH A SIZE OF (2, 2)
//Extra Cull Margin on the quad should be turned up all the way!
shader_type spatial;
render_mode unshaded;
uniform int outline_mode : hint_range(1, 3, 1) = 3;
uniform float outline_intensity : hint_range(0, 5) = 1;
uniform bool _round = false;
uniform float outline_bias : hint_range(-10, 10) = 0;
uniform vec4 outline_color : hint_color = vec4(0.0, 0.0, 0.0, 1.0);
void vertex() {
POSITION = vec4(VERTEX, 1.0);
}
void fragment() {
ALBEDO = outline_color.rgb;
vec2 screen_size = vec2(textureSize(SCREEN_TEXTURE, 1));
float px = 0.5/screen_size.x;
float py = 0.5/screen_size.y;
float d = texture(DEPTH_TEXTURE, SCREEN_UV).x;
float du = texture(DEPTH_TEXTURE, SCREEN_UV+vec2(0.0, py)).x;
float dd = texture(DEPTH_TEXTURE, SCREEN_UV+vec2(0.0, -py)).x;
float dr = texture(DEPTH_TEXTURE, SCREEN_UV+vec2(px, 0.0)).x;
float dl = texture(DEPTH_TEXTURE, SCREEN_UV+vec2(-px, 0.0)).x;
if (outline_mode == 1){
ALPHA = 0.0 + abs(abs(d)-abs(du)) + abs(abs(d)-abs(dd)) + abs(abs(d)-abs(dl)) + abs(abs(d)-abs(dr));
ALPHA *= 1000.0*outline_intensity;
} else if (outline_mode == 2) {
ALPHA = 0.0 + abs(abs(abs(d)-abs(du)) - abs(abs(d)-abs(dd))) + abs(abs(abs(d)-abs(dl)) - abs(abs(d)-abs(dr)));
ALPHA *= 3.0*50000.0*outline_intensity;
} else if (outline_mode == 3) {
float dq = texture(DEPTH_TEXTURE, SCREEN_UV+vec2(-px, py)).x;
float de = texture(DEPTH_TEXTURE, SCREEN_UV+vec2(px, py)).x;
float dz = texture(DEPTH_TEXTURE, SCREEN_UV+vec2(-px, -py)).x;
float dc = texture(DEPTH_TEXTURE, SCREEN_UV+vec2(px, -py)).x;
ALPHA = 0.0 + abs(abs(abs(d)-abs(du)) - abs(abs(d)-abs(dd))) + abs(abs(abs(d)-abs(dl)) - abs(abs(d)-abs(dr))) + abs(abs(abs(d)-abs(dq)) - abs(abs(d)-abs(dc))) + abs(abs(abs(d)-abs(dz)) - abs(abs(d)-abs(de)));
ALPHA *= 50000.0*outline_intensity;
}
ALPHA += outline_bias;
if (_round) {
ALPHA = round(ALPHA);
}
ALPHA *= outline_color.a;
}
//Written by Warren Jennings
Awesome shader, thanks for sharing! One thing I added at the end was
For my scene I set the bias very negative, which was causing the picture to become overexposed until I clamped alpha.
Thank you, kind sweet saint. I was racking my brain trying to fix it and I hail marry’d to the comments, only to see you, a shining beacon of light, arms open and ready to save my poor soul.
I shall remember your name forever
This shader is simply AWESOME!
Thank you.
Hi, good morning, very amazing shader, thanks for sharing the same, how can I do this directly for a single object, I’m creating the first shaders, and I would like to implement this contour method if it’s not a clear problem?
Awesome!!
trying to add this shader to godot 4.0 by simply including the slight changes to the variable scheme (SCREEN_TEXTURE, DEPTH_TEXTURE, source_color, etc), causes a very odd and severe amount of artifacting. not sure what’s going on there haha
I’m sure you figured it out by now, but I would look at disabling TAA anti-aliasing. That worked for me.
Thank you, this was precisely what I needed. I may have stumbled on a solution to widening the outline. In display mode 3, just multiply the screen_uv offset vectors with some float value.
Like this:
(repeat for all 8 of them)