How can I create a non-overlapping transparency shader in 3D?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By enceladus

When you set the alpha to something like 0.5 in a shader, it doesn’t look right with complex meshes because you get this behavior where when two parts of the mesh overlap (for example, where one branch of a tree is in front of another branch), they can both be seen, and the area of overlap is more opaque than the rest of the mesh. Basically, I’m looking to achieve the effect demonstrated in this video, but in 3D, and within a single mesh, rather than with two separate objects.

I found a general explanation of how to achieve something similar in GLSL in the top answer this StackOverflow question, but I’m not sure how or if this can be done in Godot. Important to note is that project is currently running on Godot 3.5; I know they made some changes to the shading language in 4.0.

Have you checked GodotShaders.com yet? Usually a lot of open source stuff there.

SnapCracklins | 2023-06-18 17:47

I checked there but couldn’t find anything that did quite what I want, but I figured out a way to do it using viewport textures!

enceladus | 2023-06-18 21:43

:bust_in_silhouette: Reply From: enceladus

Update: I found a way to get the effect I’m going for using a viewport texture. For anyone who finds this question through their search results, here’s what I did (note that I’m still using Godot 3.5, so this might work a little differently in 4.x, but the same ideas should apply):

  1. Add a viewport node as a child of the main camera. Set its size to the screen resolution in your project settings and make sure “transparent background” and “keep 3D linear” are checked.
  2. Set the mesh you want to be transparent to its own visibility layer, and set the main camera’s cull mask so it can’t see the mesh.
  3. Add a camera as a child of the viewport, and set its cull mask so that it can ONLY see the transparent mesh (see comment below for more details).
  4. Add a plane mesh as a child of one of the cameras and give it a shader material with the following code and set its albedo texture to a viewport texture pointing to the viewport you created:

.

shader_type spatial;
render_mode unshaded,diffuse_burley,specular_disabled;
uniform sampler2D texture_albedo : hint_albedo;
uniform float alpha : hint_range(0.0, 1.0) = 0.5;

void fragment() {
    vec2 uv = SCREEN_UV;
    vec4 albedo_tex = texture(texture_albedo,uv);
    ALBEDO = albedo_tex.rgb;
    ALPHA = albedo_tex.a * alpha;
}

You can then use the main camera’s cull mask and the viewport plane’s visibility property to toggle between normal view and transparent view, as well as animating the opacity by keying the alpha property of the shader material in an animation player.

One further note about setting the cull mask of the second camera so that it can only see what you’re making transparent: there’s pros and cons to making it so it can only see the transparent mesh vs. letting it see everything. If it can only see the transparent mesh, that could result in drawing the transparent mesh over things that are supposed to be in front of it; to remedy this, you could give the camera access to the entire scene, but this would make the whole screen pixelated or blurry when running at resolutions higher than your project resolution, e.g. when someone runs the game at 4k and your project resolution is 1080p. Again, I’ve not tried to do this in Godot 4, so I don’t know if they’ve fixed the issue where viewports don’t properly scale with monitor resolution. If anyone’s reading this, you’ll have to experiment for yourself.

enceladus | 2023-06-19 04:36