A Godot 4 shader to make things appear on top of other things within a range.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// A Godot 4 shader to make things appear on top of other things within a range. | |
// Initially, this was made so my characters' facial features would be rendered on top of their hair. | |
shader_type spatial; | |
render_mode unshaded; | |
// Depth buffer management. This HAS to match your camera's settings or it won't work! | |
// You can also make them global in the project settings | |
uniform float z_near = 0.1; | |
uniform float z_far = 500.0; | |
uniform sampler2D depth_texture : hint_depth_texture, repeat_disable, filter_nearest; | |
// Maximum depth we can overdraw relative to the object original depth, in ENGINE UNITS. | |
// For example, if overdraw_depth_limit = 0.2 and the original depth of the object is 1, | |
// the object will be drawn on top of everything that as a depth between 1.0 and 1.2 | |
uniform float overdraw_depth_limit : hint_range(0.0, 1.0) = 0.005; | |
// Function taken from stackoverflow : https://stackoverflow.com/a/51137756 | |
float linearize(float depth) { | |
return z_near * z_far / (z_far + depth * (z_near - z_far)); | |
} | |
void fragment() { | |
// Get the depth difference between the object we want to overdraw and the other objects | |
float screen_depth = textureLod(depth_texture, SCREEN_UV, 0.0).r; | |
float fragment_depth = FRAGCOORD.z; | |
float depth_delta = fragment_depth - screen_depth; | |
// Convert to engine units | |
float l_screen_depth = linearize(screen_depth); | |
float l_fragment_depth = linearize(fragment_depth); | |
float l_depth_delta = l_fragment_depth - l_screen_depth; | |
DEPTH = fragment_depth; // Set DEPTH default value | |
if (l_depth_delta > 0.0 && l_depth_delta < overdraw_depth_limit) { // Fragment is behind geometry, but in the overdraw zone | |
DEPTH = screen_depth - depth_delta; // Skip the inverse linearize operation and reuse the screen depth directly. That's a bit hacky though. | |
} | |
// Change the rest to suit your needs | |
ALBEDO = vec3(0.01); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment