3D X-Ray shader affecting the shadows

Godot Version

v4.3.stable.official [77dcf97d8]

Question

Hello! I made the following x-ray shader:

Note: Code may not work, I didn’t tested, just pasted the x-ray snippet from a more complete code that works in the actual project.

shader_type spatial;
render_mode cull_disabled;

const float XRAY_RADIUS = 0.5;
const float XRAY_RADIUS_Z_AXIS = 0.15;
const float XRAY_CLEAR_FACTOR = 0.9;
const float XRAY_DITHERING_SIZE = 2.0;

global uniform vec3 player_world_position;
global uniform vec2 player_screen_position;
uniform bool enable_xray = false;

void fragment() {
	if (enable_xray) {
        vec2 frag_coord = FRAGCOORD.xy;

        if (player_world_position.z < NODE_POSITION_WORLD.z && length(player_world_position) > 0.0) {
            float _size_factor = clamp(abs(player_world_position.z - NODE_POSITION_WORLD.z), 0.0, 1.0);
            float _radius = VIEWPORT_SIZE.y * XRAY_RADIUS_Z_AXIS * _size_factor;
            float _distance = distance(player_screen_position * VIEWPORT_SIZE, frag_coord);

            if (_distance < _radius) {
                if (_distance < _radius * XRAY_CLEAR_FACTOR) {
                    discard;
                } else if (int(mod(floor(frag_coord.x / XRAY_DITHERING_SIZE) + floor(frag_coord.y / XRAY_DITHERING_SIZE), 2.0)) == 0) {
                    discard;
                }
            }
        }
	}
}

This is used to make a basic x-ray effect in a top down game:


This works fine, but it also affects the shadows (it’s pretty noticeable on smaller shadow atlas sizes):

Note: Using ALPHA = 0.0 instead of discard yields the same result.

Any thoughts on how to make the x-ray work without affecting the shadows?

2 Likes

The proper way to do this would be to do the alpha cutout only if you’re not in the shadow pass of the shader. I thought there was a way to do this, but I haven’t managed yet to find any information or documentation about it.

You can try to wrap the alpha logic in

#if !defined(MODE_RENDER_DEPTH)
<your alpha logic>
#endif

and see if it works. however this uses shader internals so it may not work in future versions.

1 Like

Hi, and thanks for the reply. Unfortunately I get the same result as before when using this pre-processor condition. :confused:

Alright. Looks like this isn’t possible right now. Allow users to provide a custom shadow shader · Issue #4443 · godotengine/godot-proposals · GitHub

1 Like

This answer got a nice hack:

bool is_shadow_pass = PROJECTION_MATRIX[2][3] == 0.0;

They plan to add a global variable called IN_SHADOW_PASS, but in the meantime this hack actually fixes the issue by only discarding the fragment when not in the shadow pass. Thanks for the GitHub issue link! :heart: