How to prevent "discard" from affecting realtime shadows ?
Asked Answered
N

10

0

Hello,

I'm wondering if it is possible to prevent "discard" from affecting realtime shadows (see gif above).

In unity, realtime shadows are not affected by discard. You need to use "addshadow" keyword to get this behaviour.

addshadow - Generate a shadow caster pass. Commonly used with custom vertex modification, so that shadow casting also gets any procedural vertex animation. Often shaders don’t need any special shadows handling, as they can just use shadow caster pass from their fallback.

Thanks for your help ! 🙂

Never answered 23/4, 2023 at 17:28 Comment(0)
G
0

This is typically resolved by using an additional shadow caster object that is otherwise completely invisible except for the shadow it casts. You can do this by duplicating your mesh you want to cast shadow then in the inspector scroll down to the geometry instance section, un-collapse the geometry subheader and find the cast_shadow property. Check it's dropdown list to find a shadows only option there.

Next for extra optimization you can now disable the shadow casting on the original mesh instance since the duplicate already casts it. And to make it easier to work with you can parent them so the shadow caster is child of the other mesh.

Also going to leave here another remark as an aside. In your .gif the shadow is coming from the shaded side only, which of course makes sense, but if you did want this effect but for all the mesh faces to cast the shadow you would use the double sided option from the same property list mentioned above.

Gawky answered 23/4, 2023 at 23:44 Comment(0)
S
0

You might also be able to write to the depth buffer in the fragment shader. I'm just not sure how Godot handles the discard in that case.

Sow answered 24/4, 2023 at 6:41 Comment(0)
N
0

Thanks for your answers 🙂

Gawky Your answer did the trick but I realized that it is not what I need exactly.

The "issue" has been solved by bgolus when I asked for help on the Unity forum here

[...] detect in the shader if you're rendering a shadow map or the camera depth texture.

He suggested to write this just before discarding:

#ifdef UNITY_PASS_SHADOWCASTER
   #ifdef SHADOWS_DEPTH
      if (unity_LightShadowBias.z != 0.0) return; // is shadow caster
   #endif
#endif

I think I get the idea but not sure how to handle it in Godot...

Thanks for your time!

Never answered 24/4, 2023 at 16:8 Comment(0)
S
0

I think Begley answer is correct though. Without writing a custom shader, you can just use 2 objects.

Sow answered 24/4, 2023 at 19:28 Comment(0)
E
1

Never

I recently made a blog post about a way to do this in Godot: https://blog.vortexbasis.com/detecting-shadow-pass-in-godot-shader

You need a way to return 1 for opacity in the shadow pass, but return 0 for the main pass. There's no "official" way to do this currently, but I noticed that near_z is set to 0 for the shadow passes, so if you are using perspective cameras, you can do the following in your shader to detect when you are in a shadow pass:

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

There's an open issue from about a year ago where there was a discussion about possibly adding an AT_SHADOW_PASS define:
https://github.com/godotengine/godot-proposals/issues/4443

Euphrasy answered 28/11, 2023 at 18:2 Comment(0)
F
0

Euphrasy What guarantees that the near z is always 0 in the shadow pass?

Functionary answered 28/11, 2023 at 18:16 Comment(0)
E
0

Functionary Well, it's really just meant as a workaround until there is an official flag for AT_SHADOW_PASS. I was just digging through the source code for Godot and noticed that two differences for _render_shadow_pass (godot/drivers/gles3/rasterizer_scene_gles3.cpp) were that the camera transform is set to the transform for the light (instead of the camera) and z_near is explicitly set to 0.0. The formula for PROJECTION_MATRIX[3][2] multiplies the numerator by the z_near value so that will be zero if z_near is 0 (but it also assumes you are using a perspective camera with non-zero z_near to be able to differentiate on that). You can alternatively stash your active actual camera position into a global shader parameter each frame and then compare that against the "camera_position_world" built-in to see if they are different (since the latter will be position of the light in the shadow pass).

If you use something like this in multiple places, you might want to wrap that logic in a VisualShaderNodeCustom so you can just swap that out for a change to an official flag like AT_SHADOW_PASS in the future. I'm only about a month into learning Godot, but if no one has added the AT_SHADOW_PASS after a while, I might eventually try my hand at submitting a pull request for that once I learn where things go a bit better.

Euphrasy answered 29/11, 2023 at 1:28 Comment(0)
F
0

Euphrasy and z_near is explicitly set to 0.0

This will only nullify PROJECTION_MATRIX[3][2] for omni lights matrices. Not sure if it'll work for ortho matrices used with directional lights. Have you tested it for directional lights too?

Functionary answered 29/11, 2023 at 1:50 Comment(0)
E
0

Functionary It is working with the single directional light in my test scene. When my character enters a building, I fade an "xray" instance parameter from 0 to 1 on the outer walls of the building that are facing the camera to make them fade out with a dither pattern. Without the workaround above, the sun directional light shines into the building. With the workaround above where I always return 1 for opacity when (PROJECTION_MATRIX[3][2] == 0.0), then the walls block the directional light. I was working on recreating a setup I formerly had in Unreal Engine where I could use the "Shadow Pass Switch" to achieve the same effect.

I haven't tried with orthographic cameras, so I don't know whether it works with that.

Euphrasy answered 29/11, 2023 at 3:15 Comment(0)
E
0

UPDATE:
Upon further experimentation, just having this doesn't necessarily seem to solve my issue. This blocks the light without issues for my directional light (as seen in picture above). For omni lights, however, it seems to work at first, but every now and then the light acts as though the wall isn't there but then if I move my camera around, and it goes back to behaving the way I want (which is like the Shadow Pass Switch in Unreal).

Euphrasy answered 8/12, 2023 at 21:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.