Decal Example

A relatively simple decal shader that uses the depth buffer to draw over the geometry in the scene. The shader works great with a default Godot cube.

One thing to note: The shader does NOT take the normals of the scene into account and will draw over anything within the cube. I tried writing the shader so it would reconstruct the normals from the depth buffer (which kinda worked) but the normals were relative to the camera rotation and not the world position and I couldn’t figure out how to transform it for use with decals.

For the drawing over anything within the cube, color masking could be used for this! I didn’t implement it in the shader, but I have written a tutorial on colormasking on my website RandomMomentania, which may be helpful as a reference.

 

This shader is not optimized! I just wrote it as a shader experiment/test and wanted to share the results.

Credit to Andon M. Coleman on StackOverflow for posting the snippet for getting a world position from the depth buffer. Also, credit to Ronja’s tutorials for the inspiration to make this little shader experiment.

Shader code
shader_type spatial;
render_mode world_vertex_coords, unshaded;
uniform vec4 albedo : hint_color;
uniform sampler2D texture_albedo : hint_albedo;

uniform float cube_half_size = 1.0;


// Credit: https://stackoverflow.com/questions/32227283/getting-world-position-from-depth-buffer-value
vec3 world_pos_from_depth(float depth, vec2 screen_uv, mat4 inverse_proj, mat4 inverse_view) {
	float z = depth * 2.0 - 1.0;
	
	vec4 clipSpacePosition = vec4(screen_uv * 2.0 - 1.0, z, 1.0);
	vec4 viewSpacePosition = inverse_proj * clipSpacePosition;
	
	viewSpacePosition /= viewSpacePosition.w;
	
	vec4 worldSpacePosition = inverse_view * viewSpacePosition;
	
	return worldSpacePosition.xyz;
}

void fragment() {
	float depth = texture(DEPTH_TEXTURE, SCREEN_UV).x;
	vec3 world_pos = world_pos_from_depth(depth, SCREEN_UV, INV_PROJECTION_MATRIX, (CAMERA_MATRIX));
	vec4 test_pos = (inverse(WORLD_MATRIX) * vec4(world_pos, 1.0));
	
	if (abs(test_pos.x) > cube_half_size ||abs(test_pos.y) > cube_half_size || abs(test_pos.z) > cube_half_size) {
		discard;
	}
	ALBEDO = texture(texture_albedo, (test_pos.xz * 0.5) + 0.5).rgb * albedo.rgb;
}
Tags
decal, godot
The shader code and all code snippets in this post are under MIT license and can be used freely. Images and videos, and assets depicted in those, do not fall under this license. For more info, see our License terms.

Related shaders

Texture mix example

guest
2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
NekotoArts
NekotoArts
1 month ago

icon.png has seen better days XD. Nice work tho!