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;
}
icon.png has seen better days XD. Nice work tho!
Thanks NekotoArts! I agree on the icon, I just needed something for testing XD
Great starting point for decal shaders!
An optimization tip is to inverse the world matrix in the vertex shader and send it to fragment in a varying mat4 instead of inverting it in the fragment shader. This yields a massive speedup, at least on the fairly bad graphics card I’ve got in this laptop…
Thanks! With unoptimised heavy use, I got about 10 fps on a low end machine with just that tip.
Cool! How can you apply it to only certain objects?
Is this more performant than using a Decal?
im using it bc i want to use opengl and in opengl theres no decals other then that im not sure