Translucent overlapping 3D objects sometimes partially disappearing

helloCLhelloCL Posts: 2Member

Hello!

I'm using the Godot Engine to create a 3D map editor for another game and made a grid system for placing objects. The grid and objects are both translucent, which is currently causing some visibility issues.

https://streamable.com/hmk38v

In the video above, the grid is causing the far half of the cube to intermittently disappear. It appears to be dependent on the position of the camera, but doesn't show any discernible pattern as to when the disappearing issue occurs. I think the issue is related to the shaders I've made, but I'm not entirely sure, as I'm not too familiar with working with shaders.

Below are: the material used on the grid squares and the material + shader used for the cube object.
Note: the purpose of the shader is to allow the cube object to fade in and out as it comes along the track. In this particular situation, the ALPHA value for the cube should be entirely dependent on the alpha value of the vec4 albedo shader parameter.

Cube Shader

shader_type spatial;
render_mode cull_back, depth_draw_always, diffuse_burley, specular_schlick_ggx;

const float PI = 3.14159265359;

uniform vec4 albedo : hint_color = vec4(1.0);
uniform sampler2D textureAlbedo : hint_albedo;
uniform float specular;
uniform float metallic;
uniform float roughness : hint_range(0,1);

uniform bool useFade;
uniform float positiveZCutoff;
uniform float negativeZCutoff;
uniform float cutoffDistance : hint_range(0.1, 40.0);
uniform float staticCutoffAlpha : hint_range(0.0, 1.0);

void fragment()
{
    vec4 albedoTex = texture(textureAlbedo, UV);
    ALBEDO = albedo.rgb * albedoTex.rgb;

    float z = (CAMERA_MATRIX * vec4(VERTEX, 1.0)).z;
    if (useFade)
    {
        if (z > positiveZCutoff)
        {
            float weight = min(1.0, (z - positiveZCutoff) / cutoffDistance);
            ALPHA = (cos(weight * PI) + 1.0) / 2.0 * albedo.a * albedoTex.a;
        }
        else if (z < negativeZCutoff)
        {
            float weight = min(1.0, (negativeZCutoff - z) / cutoffDistance);
            ALPHA = (cos(weight * PI) + 1.0) / 2.0 * albedo.a * albedoTex.a;
        }
        else
        {
            ALPHA = albedo.a * albedoTex.a;
        }
    }
    else
    {
        if (z > positiveZCutoff + cutoffDistance)
        {
            ALPHA = 0.0;
        }
        else if (z > positiveZCutoff)
        {
            ALPHA = staticCutoffAlpha * albedo.a * albedoTex.a;
        }
        else if (z < negativeZCutoff)
        {
            // always fade negative cutoff
            float weight = min(1.0, (negativeZCutoff - z) / cutoffDistance);
            ALPHA = (cos(weight * PI) + 1.0) / 2.0 * albedo.a * albedoTex.a;
        }
        else
        {
            ALPHA = albedo.a * albedoTex.a;
        }
    }

    METALLIC = metallic;
    ROUGHNESS = roughness;
    SPECULAR = specular;
}

Cube ShaderMaterial

[gd_resource type="ShaderMaterial" load_steps=2 format=2]

[ext_resource path="res://CubeShader.shader" type="Shader" id=1]

[resource]
shader = ExtResource( 1 )
shader_param/albedo = Color( 0.784314, 0, 0, 0.392157 )
shader_param/specular = 0.5
shader_param/metallic = 0.3
shader_param/roughness = 0.8
shader_param/useFade = true
shader_param/positiveZCutoff = 4.0
shader_param/negativeZCutoff = -4.0
shader_param/cutoffDistance = 4.0
shader_param/staticCutoffAlpha = 0.25

Grid Square SpatialMaterial

[gd_resource type="SpatialMaterial" format=2]

[resource]
flags_transparent = true
flags_unshaded = true
params_diffuse_mode = 4
params_specular_mode = 4
params_cull_mode = 2
params_depth_draw_mode = 1
albedo_color = Color( 0.392157, 0.392157, 0.392157, 0.235294 )

Best Answers

  • cyberealitycybereality Posts: 1,049
    edited August 5 Accepted Answer

    Overlapping transparent objects are really difficult in just about any engine. I didn't look at your code, but I'm familiar with the problem. Usually what engines do is draw all the opaque objects first with depth write on and depth test on, then in a second pass, draw the transparent objects with depth test on but depth write off, and in back to front order. If you have two intersecting transparent objects, then how they appear will be determined by which one is closer to the camera. That is why you get that problem when the center points of two objects are close together (similar to z-fighting).

    It's not an easy thing, because even in the best case the color mixing might be off. However, if you adjust the depth settings on the material, you might get a better look. I think it's called depth pre-pass or something like that. It might help or might not, I haven't actually come across this issue in Godot. But probably the better solution is to adjust the game so transparent objects don't overlap.

  • helloCLhelloCL Posts: 2
    Accepted Answer

    @Megalomaniak said:
    It's probably something todo with your ZCutoff. Maybe this:

        if (z > positiveZCutoff + cutoffDistance)
            {
                ALPHA = 0.0;
            }
    

    I don't think that is the case, since the grid is positioned right at the world Z origin, the cubes are slightly under 1u in each dimension, and the positive and negative Z cutoffs were at 4 and -4 respectively. But thank you for the reply!

    @cybereality said:
    Overlapping transparent objects are really difficult in just about any engine. I didn't look at your code, but I'm familiar with the problem. Usually what engines do is draw all the opaque objects first with depth write on and depth test on, then in a second pass, draw the transparent objects with depth test on but depth write off, and in back to front order. If you have two intersecting transparent objects, then how they appear will be determined by which one is closer to the camera. That is why you get that problem when the center points of two objects are close together (similar to z-fighting).

    It's not an easy thing, because even in the best case the color mixing might be off. However, if you adjust the depth settings on the material, you might get a better look. I think it's called depth pre-pass or something like that. It might help or might not, I haven't actually come across this issue in Godot. But probably the better solution is to adjust the game so transparent objects don't overlap.

    Thanks for the explanation!

    That is why you get that problem when the center points of two objects are close together (similar to z-fighting).

    This point in particular was really insightful. I've done a test where I moved the cube forward by a tiny amount and (at least when facing the grid from the intended front side) that prevented the cube from partially disappearing. Although, this means that viewing the grid from the backside would mean that the cube would always have a portion missing.

    While testing this, I also came across a potential solution. I raised the render priority of the grid so that it is always rendered in front of the translucent blocks. At the moment, it appears to fix the issue and there aren't any visual oddities, so I'll keep this for now unless another issue pops up.

    Thanks again to you both!

Answers

  • MegalomaniakMegalomaniak Posts: 2,898Admin

    It's probably something todo with your ZCutoff. Maybe this:

        if (z > positiveZCutoff + cutoffDistance)
            {
                ALPHA = 0.0;
            }
    
  • cyberealitycybereality Posts: 1,049Moderator
    edited August 5 Accepted Answer

    Overlapping transparent objects are really difficult in just about any engine. I didn't look at your code, but I'm familiar with the problem. Usually what engines do is draw all the opaque objects first with depth write on and depth test on, then in a second pass, draw the transparent objects with depth test on but depth write off, and in back to front order. If you have two intersecting transparent objects, then how they appear will be determined by which one is closer to the camera. That is why you get that problem when the center points of two objects are close together (similar to z-fighting).

    It's not an easy thing, because even in the best case the color mixing might be off. However, if you adjust the depth settings on the material, you might get a better look. I think it's called depth pre-pass or something like that. It might help or might not, I haven't actually come across this issue in Godot. But probably the better solution is to adjust the game so transparent objects don't overlap.

  • helloCLhelloCL Posts: 2Member
    Accepted Answer

    @Megalomaniak said:
    It's probably something todo with your ZCutoff. Maybe this:

        if (z > positiveZCutoff + cutoffDistance)
            {
                ALPHA = 0.0;
            }
    

    I don't think that is the case, since the grid is positioned right at the world Z origin, the cubes are slightly under 1u in each dimension, and the positive and negative Z cutoffs were at 4 and -4 respectively. But thank you for the reply!

    @cybereality said:
    Overlapping transparent objects are really difficult in just about any engine. I didn't look at your code, but I'm familiar with the problem. Usually what engines do is draw all the opaque objects first with depth write on and depth test on, then in a second pass, draw the transparent objects with depth test on but depth write off, and in back to front order. If you have two intersecting transparent objects, then how they appear will be determined by which one is closer to the camera. That is why you get that problem when the center points of two objects are close together (similar to z-fighting).

    It's not an easy thing, because even in the best case the color mixing might be off. However, if you adjust the depth settings on the material, you might get a better look. I think it's called depth pre-pass or something like that. It might help or might not, I haven't actually come across this issue in Godot. But probably the better solution is to adjust the game so transparent objects don't overlap.

    Thanks for the explanation!

    That is why you get that problem when the center points of two objects are close together (similar to z-fighting).

    This point in particular was really insightful. I've done a test where I moved the cube forward by a tiny amount and (at least when facing the grid from the intended front side) that prevented the cube from partially disappearing. Although, this means that viewing the grid from the backside would mean that the cube would always have a portion missing.

    While testing this, I also came across a potential solution. I raised the render priority of the grid so that it is always rendered in front of the translucent blocks. At the moment, it appears to fix the issue and there aren't any visual oddities, so I'll keep this for now unless another issue pops up.

    Thanks again to you both!

  • MegalomaniakMegalomaniak Posts: 2,898Admin

    Ah, didn't notice that there were two overlapping objects there. This is to be expected then, and draw order/render priority is the appropriate solution.

Leave a Comment

BoldItalicStrikethroughOrdered listUnordered list
Emoji
Image
Align leftAlign centerAlign rightToggle HTML viewToggle full pageToggle lights
Drop image/file