Multi-mesh fading / transparency

Godot Version

v4.2.1.stable.official [b09f793f5]

Question

Hello everyone !

My friend and I are currently developing our 3D game and are encountering an issue that we are having trouble solving. I’m currently working on how the camera works, including how to handle the player disappearing when the camera gets too close to them. Our model is how this operation works in Zelda BotK and TotK where Link’s 3D model, as well as all of this equipment (weapons and armor) gradually disappear. Note how all the elements making up the player “Link” seem to form a whole.


Our test player scene is composed of 3 simple mesh, a capsule (the body), a cone (like a nose) and a plane crossing halfway through the capsule to test the proper functioning of the transparency.

Our first naive solution was of course to simply increase the transparency of the different elements making up our player. However, and obviously, this does not work since we now observe all the models transparently through each other. As if we could observe Link’s model in transparency in his shield itself in transparency (which is not the case).

We then tried to implement this through a simple spatial shader, notably by exploiting the “depth_draw_always” and “depth_prepass_alpha” render_modes:

shader_type spatial;
render_mode depth_draw_always;
render_mode depth_prepass_alpha;

uniform float alpha = 1.0;
uniform vec3 albedo : source_color;

void fragment()
{
     ALBEDO = albedo.rgb;
     ALPHA = alpha;
}

This is an improvement as at certain angles the desired effect is achieved.
From certain angle, the transparency effect does not work, we would not like/should not see the nose and the part of the plane in the capsule.
From other angle (same configuration and same shader), we notice that the effect is functional. So this first approach is clearly not constant.

We then tested the render_priority and sorting_offset parameters, allowing us to obtain generally satisfactory results from all angles, but also necessarily depending on the observation angle. By setting a render priority to 0 for the body and to 1 for the nose and the plane and using the same shader as above:

(https://image.noelshack.com/fichiers/2024/08/7/1708877367-render-priority-0.png)
From this angle, everything is perfect, the three elements are transparent and we cannot observe the nose and the part of the plane through the body.

(https://image.noelshack.com/fichiers/2024/08/7/1708877367-render-priority-1.png)
But obviously from this angle, the effect no longer works, we should not see the body through the right part of the plane and the left base of the nose

Our conclusion is therefore that, in the current state of the engine, the desired transparency effect does not seem to us to be achievable by the use of shaders and the manipulation of the alphas of the different meshes. The only solution would be for all of the elements making up the player’s 3D model to be in a single Mesh (and therefore Material), but not very practical in practice.
Please tell me if I’m wrong on this point and if we missed a few things about the operation and possibilities of Godot in this context (transparency / alpha + shader / material)


Our second idea was to manage the rendering of the player independently and following the rendering of the rest of the game, so that the transparency is applied to the total rendering of the player and not element by element as in the previous case of shaders.

If I’m not mistaken (again, correct me if I’m wrong), Godot does not support deferred rendering. We therefore tried an alternative by rendering the player through another camera and subviewport and overlaying the rendering of this subviewport on the main. Therefore, by managing the transparency of the subviewport, we obtain the desired effect:


We try the following configuration :

  • All the meshes of the player have their VisualInstance3D layer set to 2
  • The VisualInstance3D layer of the DirectionalLight3D is set to 1 and 2
  • All other meshes of the 3D scene have their VisualInstance3D layer set to 1

– The main camera, rendering the whole scene except the player, has its CullMask set to 1 only
– A second camera, child of SubViewport, rendering only the player, has its CullMask set to 2 only

The main problem with this approach is that the player no longer casts a shadow, and above all, no longer any shadow cast by the rest of the scene is applied to him.


We are stuck at this point and we don’t see how to achieve the result we want, the perfectly managed transparency of the second approach with the full shadow rendering of the first, if this is even possible with the Godot engine in its current form.

Do you have any ideas, solutions or suggestions to help and guide us?

Thank you in advance and have a good weekend!