1
\$\begingroup\$

I have an issue when using textures in OpenGL, strange artifacts occur where geometry overlaps, but not always. Video Reference. I am using a GL_TEXTURE_2D with GL_ARB_image_load_store to make a custom depth test shader that stores material data for opaque and transparent geometry. The video given shows the artifacts occur where the support structure for a table is occluded behind the top of the table, but strangely, not occurring where the base of the table is occluded by the support.

#version 450 core

in VS_OUT {
    vec3 Position;
    vec3 Normal;
    vec2 TexCoords;

    mat3 TanBitanNorm;
} fs_in;

// Material data
uniform sampler2D uAlbedoMap;
uniform sampler2D uNormalMap;
uniform sampler2D uMetallicMap;

// Material info out
layout(rgba16f) coherent uniform image2D uAlbedoDepthOpaque;
layout(rgba16f) coherent uniform image2D uNormalMetallicOpaque;
layout(rgba16f) coherent uniform image2D uAlbedoDepthTransparent;
layout(rgba16f) coherent uniform image2D uNormalAlphaTransparent;

// Depth info in/out
layout(r8) uniform image2D uDepthBufferOpaque;
layout(r8) uniform image2D uDepthBufferTransparent;

void main() {
    vec3 n_tex = texture(uNormalMap, fs_in.TexCoords).xyz;
    n_tex = n_tex * 2.0f - 1.0f;

    ivec2 tx_loc = ivec2(gl_FragCoord.xy);
    const float opaque_depth = imageLoad(uDepthBufferOpaque, tx_loc).r; // Stored depth of opaque
    const float trans_depth = imageLoad(uDepthBufferTransparent, tx_loc).r; // Stored depth of transparent

    // Depth processing
    if (gl_FragCoord.z > opaque_depth) {
        bool tran = false;
        if (trans_depth > opaque_depth)
            tran = trans_depth > gl_FragCoord.z;
        else
            tran = true;

        // Transparent
        if (texture(uAlbedoMap, fs_in.TexCoords).a < 1.0f && tran) {
            imageStore(uDepthBufferTransparent, tx_loc,
                vec4(gl_FragCoord.z));

            imageStore(uAlbedoDepthTransparent, tx_loc,
                vec4(texture(uAlbedoMap, fs_in.TexCoords).rgb, gl_FragCoord.z));
            imageStore(uNormalAlphaTransparent, tx_loc,
                vec4(abs(length(n_tex) - 1.0f) > 0.1f ? fs_in.Normal : normalize(fs_in.TanBitanNorm * n_tex), texture(uAlbedoMap, fs_in.TexCoords).a));
        }

        // Opaque
        else {
            imageStore(uDepthBufferOpaque, tx_loc,
                vec4(gl_FragCoord.z));

            imageStore(uAlbedoDepthOpaque, tx_loc,
                vec4(texture(uAlbedoMap, fs_in.TexCoords).rgb, gl_FragCoord.z));
            imageStore(uNormalMetallicOpaque, tx_loc,
                vec4(abs(length(n_tex) - 1.0f) > 0.1f ? fs_in.Normal : normalize(fs_in.TanBitanNorm * n_tex), texture(uMetallicMap, fs_in.TexCoords).r));
        }
    }

    if (opaque_depth == 0.0f) {
        imageStore(uDepthBufferOpaque, tx_loc,
            vec4(0.125f));
    }

    else {
        imageStore(uDepthBufferOpaque, tx_loc,
            vec4(0.125f + opaque_depth));
    }
}

Render with overlapping geometry shows that artifacts still occur outside of reading from the texture. Also in the video, I move the camera back and forth (with orthographic projection) and the artifacts become brighter and darker. Render with overlapping geometry w/out depth processing shows that the brighter/darker values were from the depth test.

Any ideas on why this occurs, and how can I fix it?

\$\endgroup\$
  • \$\begingroup\$ This looks like Z-fighting from the legs of the table occasionally mapping to the same depth range as the table top. This can happen if your near and far planes are very far apart. Have you tried clamping them in to hug the scene more tightly? \$\endgroup\$ – DMGregory Jul 13 at 2:15
  • \$\begingroup\$ I have the near and far planes really close currently. -10 units and 10 units, whereas the scene is around 3x3x3 units. \$\endgroup\$ – William Jul 13 at 2:18
  • \$\begingroup\$ Both links appear dead. Can you put the pics into the question itself please? \$\endgroup\$ – Kromster Jul 13 at 5:15
  • \$\begingroup\$ All three links work for me (Even cleared cookies), and I would put them into the question, but they are videos. I figured streamable would work fine. \$\endgroup\$ – William Jul 13 at 5:24
  • \$\begingroup\$ I've been thinking over what may be happening, and I think multiple fragments are being processed at the same time. If fragments can be processed simultaneously, is there a way to disable it? \$\endgroup\$ – William Jul 16 at 22:08
0
\$\begingroup\$

The issue is Z-fighting.

The gpu, when choosing which pixels to write to the back buffer, write using a sort of painter's algorithm, farthest away, to closest, for each pixel. This is called depth testing.

Normally, this would be caused by two objects occupying the same depth in the depth buffer, but you are bypassing that, in an attempt to achieve transparency.

This is my advice:

  1. Deploy a painter's algorithm to perform transparency rendering, instead of what you are doing. Furthest away to closest. This is done in your renderer, not the shader. basically take all your objects and put them in a collection, sorting in descending order, of distance from the camera.

  2. Turn OFF depth testing - This is important.

  3. Utilize built-in blending for building up your final buffer. I'm sorry, but I don't know the correct blending settings off-hand. Experiment, or look up the right bledning algorithm.

  4. Get rid of imageLoad/imageStore: you don't need them, and tbh it complicates your shader code. Use the regular shader outputs and texture() calls. It might slow down your shader code, but it will be easy to understand what is going on. Optimize later.

  5. Treat transparent and opaque as varying degrees of transparency, not different cases. See step 1.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ That's what I thought too, but the comments above make that seem unlikely. I'm wondering whether there's some kind of race condition or depth-testing bypass happening through the use of imageStore() rather than conventional fragment shader return values here. \$\endgroup\$ – DMGregory Jul 16 at 15:24
  • \$\begingroup\$ I think you are right about the race condition. The OP is trying to perform selecting depth testing per-fragmen, thus bypassing normal depth testing. Updating my answer appropriately. \$\endgroup\$ – Ian Young Jul 17 at 8:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.