Depth-Based Outline Shader

A pleasantly functional depth-based, pseudo-screen-space outline shader, based quite closely on shader by Digvijaysinh Gohi, described in this youtube video. I’ve made a couple improvements, most notably a sensitivity setting to determine what difference in camera distance merits an outline.

To make use of it you do have to use the ‘quad over camera’ trick so that the shader can access the depth buffer. That trick described both in the video and here in the Godot docs. Apply this to that quad adn you’re good to go!

Shader code
// Godot 4.4, Forward+ or Mobile
// Based quite closely on shader by Digvijaysinh Gohil
// See https://www.youtube.com/watch?v=-SXJvpbFJ7M&ab_channel=DigvijaysinhGohil
shader_type spatial;
render_mode unshaded, fog_disabled;

uniform sampler2D screen_texture : source_color, hint_screen_texture;
uniform sampler2D depth_texture : hint_depth_texture, repeat_disable;
uniform float sensitivity: hint_range(0.0, 3.0, 0.01);
uniform float outline_thickness: hint_range(0.0, 2.0, 0.01);
uniform float step_threshold: hint_range(0.0, 1.0, 0.01);
uniform vec3 outline_color: source_color;

void vertex() {
	POSITION = vec4(VERTEX.xy, 1.0, 1.0);
}

float DepthVS(vec2 uv, mat4 inv_projection_mat) {
	float depth = texture(depth_texture, uv).r;
	return 1. / (depth * inv_projection_mat[2].w + inv_projection_mat[3].w);
}

void fragment() {
	float thickness = outline_thickness * 0.001;
	float depth = DepthVS(SCREEN_UV, INV_PROJECTION_MATRIX);
	
	// Check the depth in the four cardinal and four diagonal directions to determine whether the
	// current pixel is on an edge
	float border_r = DepthVS(SCREEN_UV + vec2(thickness, 0), INV_PROJECTION_MATRIX) - depth;
	float border_l = DepthVS(SCREEN_UV + vec2(-thickness, 0), INV_PROJECTION_MATRIX) - depth;
	float border_t = DepthVS(SCREEN_UV + vec2(0, thickness), INV_PROJECTION_MATRIX) - depth;
	float border_b = DepthVS(SCREEN_UV + vec2(0, -thickness), INV_PROJECTION_MATRIX) - depth;
	float border_rt = DepthVS(SCREEN_UV + vec2(thickness, thickness), INV_PROJECTION_MATRIX) - depth;
	float border_rb = DepthVS(SCREEN_UV + vec2(thickness, -thickness), INV_PROJECTION_MATRIX) - depth;
	float border_lt = DepthVS(SCREEN_UV + vec2(-thickness, thickness), INV_PROJECTION_MATRIX) - depth;
	float border_lb = DepthVS(SCREEN_UV + vec2(-thickness, thickness), INV_PROJECTION_MATRIX) - depth;
	
	float outline = clamp(((border_r + border_l + border_t + border_b + border_rt + border_rb + border_lt + border_lb) * sensitivity) / 8. , 0., 1.);
	outline = smoothstep(0, step_threshold, outline);

	ALBEDO = outline * outline_color;
	ALPHA = outline;
}
Tags
depth, outline, screen-space
The shader code and all code snippets in this post are under CC0 license and can be used freely without the author's permission. Images and videos, and assets depicted in those, do not fall under this license. For more info, see our License terms.

More from withoutfail

Screen Space (Canvas) Monotone / Halftone

Snap Screen Colors to Palette (Posterize)

Related shaders

2D Outline and Rainbow outline 2 in 1

Sobel Outline Postprocess Shader

Object Outline Shader

Subscribe
Notify of
guest

2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Sp1cyP3pp3r
2 months ago

Thanks a lot, awesome shader

unknown
unknown
1 month ago

I dont know if I am doing anything wrong, but the shader does not draw an outline if the camera is looking in any of the 6 cardinal directions. like up, down, left, right, forwards, and backwards.