Decal shader (similar to +Forward Decal… but for Compatibility-mobile)

Godot port of this shader and this one as well..

Yes, there are some decal shaders out there, but no one is like the default Godot Forward+ Decal.. I think is very important to be able to fade the decal across Y-Axis, specially if there is no direct connection with cull-masks layers…

Shader code
shader_type spatial;
render_mode world_vertex_coords, blend_mix, depth_draw_opaque, cull_back, diffuse_lambert, specular_schlick_ggx;

uniform vec2 Scale = vec2(1.000000, 1.000000);
uniform sampler2D Albedo : source_color, repeat_disable;
uniform sampler2D Normal : source_color, hint_normal, repeat_disable;
uniform sampler2D Roughtness : source_color, hint_default_white, repeat_disable;
uniform sampler2D Emission : source_color, hint_default_black, repeat_disable;
uniform float Albedo_Mix : hint_range(0.0, 1.1, 0.1) = 1.1;

uniform float Emission_Energy : hint_range(0.0, 30, 0.1) = 1.0;
uniform vec4 Modulate : source_color = vec4(1.0,1.0,1.0,1.0);
uniform vec4 Modulate_Emission : source_color = vec4(1.0,1.0,1.0,1.0);

uniform sampler2D DEPTH_TEXTURE: hint_depth_texture;
uniform float cube_half_size = 0.5;

// Y-axis fade controls
uniform bool enable_y_fade = false; // Toggle for fade effect
uniform float fade_start = 0.3; // Where fading begins (0-1)
uniform float fade_end = 0.7;   // Where fading ends (0-1)
uniform float fade_power = 2.0; // Curve sharpness

varying mat4 INV_MODEL_MATRIX;

void vertex(){
    INV_MODEL_MATRIX = inverse(MODEL_MATRIX);
}

vec3 world_pos_from_depth(float depth, vec2 screen_uv, mat4 inverse_proj, mat4 inverse_view) {
    float z = depth;
    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;
}

float smooth_fade(float y_pos) {
    // Normalize Y position (-0.5 to 0.5) -> (0 to 1)
    float normalized_y = clamp((y_pos + cube_half_size) / (2.0 * cube_half_size), 0.0, 1.0);
    
    // Calculate fade factor
    float fade_factor = smoothstep(fade_start, fade_end, normalized_y);
    fade_factor = pow(fade_factor, fade_power);
    
    return 1.0 - fade_factor;
}

void fragment() {
    float depth = texture(DEPTH_TEXTURE, SCREEN_UV).x;
    vec3 world_pos = world_pos_from_depth(depth, SCREEN_UV, INV_PROJECTION_MATRIX, (INV_VIEW_MATRIX));
    vec4 test_pos = (INV_MODEL_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;
    }

    vec2 NEW_UV = (test_pos.xz) + 0.5;
    
    vec4 n_out3p0 = texture(Albedo, NEW_UV * Scale);
    vec3 emission_map = texture(Emission, NEW_UV * Scale).rgb;
    vec3 normal_map = texture(Normal, NEW_UV * Scale).rgb;
    float rought_map = texture(Roughtness, NEW_UV * Scale).r;

    // Default fade factor (1.0 means no fade)
    float y_fade = 1.0;
    
    // Only calculate fade if enabled
    if (enable_y_fade) {
        y_fade = smooth_fade(test_pos.y);
        if (y_fade <= 0.0) {
            discard;
        }
    }

    // Apply fade to all channels (or full strength if fade disabled)
    ALBEDO = n_out3p0.rgb * Modulate.rgb * y_fade;
    ALPHA = n_out3p0.a * Albedo_Mix * Modulate.a * y_fade;
    NORMAL = normal_map;
    EMISSION = emission_map * Emission_Energy * Modulate_Emission.rgb * y_fade;
    ROUGHNESS = rought_map;
}

// Credits: Shader based on OlisUnfinishedProjects 4.0 Decal Shader 
// https://godotshaders.com/shader/decal-shader-4-0-port/
// https://stackoverflow.com/questions/32227283/getting-world-position-from-depth-buffer-value
Tags
3d, compatibility, decal, Forward+, godot 4, Godot 4.0, LeLu, pbr
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.

Related shaders

Decal Shader for 4.3 compatibility renderer + transparency support & no repeat

Decal shader 4.0 port

Decal with PBR – Godot 4.4

Subscribe
Notify of
guest

4 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
aguy
aguy
1 month ago

can someone explain how to make it work? i can’t figure it out. i tried doing the same thing as other decals but it just does not show anything

[0]
[0]
1 month ago

Also having trouble, I have this shader copied to res://decal.gdshader. I have two MeshInstance3d one is Decal and a 1x1x1 boxmesh the other is the Floor_box 10x1x10.
the Decal is part way in the floor (have tried multiple amounts from 0.01 to 0.9). the decal image chosen is the icon.svg in this case (have tried other image types and sizes with no change) the project is a new one set to compatibility renderer on creation with no additional changes. my godot version is 4.4.1

Below is my scene example of my setup.
Let me know if you have any ideas thanks.

[gd_scene load_steps=6 format=3 uid="uid://bgynloenlrrwn"]


[ext_resource type="Shader" uid="uid://c3vd7skmp61a4" path="res://decal.gdshader" id="1_a202f"]
[ext_resource type="Texture2D" uid="uid://drm0ba47unwop" path="res://icon.svg" id="2_noarx"]


[sub_resource type="BoxMesh" id="BoxMesh_noarx"]
size = Vector3(10, 1, 10)


[sub_resource type="ShaderMaterial" id="ShaderMaterial_a0tk4"]
render_priority = 0
shader = ExtResource("1_a202f")
shader_parameter/Scale = Vector2(1, 1)
shader_parameter/Albedo = ExtResource("2_noarx")
shader_parameter/Albedo_Mix = 1.1
shader_parameter/Emission_Energy = 1.0
shader_parameter/Modulate = Color(1, 1, 1, 1)
shader_parameter/Modulate_Emission = Color(1, 1, 1, 1)
shader_parameter/cube_half_size = 0.5
shader_parameter/enable_y_fade = false
shader_parameter/fade_start = 0.3
shader_parameter/fade_end = 0.7
shader_parameter/fade_power = 2.0


[sub_resource type="BoxMesh" id="BoxMesh_r3fl7"]


[node name="Node3D" type="Node3D"]


[node name="Floor_box(10x1x10)" type="MeshInstance3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.5, 0)
mesh = SubResource("BoxMesh_noarx")


[node name="Decal" type="MeshInstance3D" parent="."]
material_override = SubResource("ShaderMaterial_a0tk4")
mesh = SubResource("BoxMesh_r3fl7")


[0]
[0]
1 month ago
Reply to  [0]
Last edited 1 month ago by [0]