Search code examples
c++opengltransparency

Rendering an object that is partially opaque and partially fully transparent


I have a problem when I'm trying to render a transparent object. I only have one mesh so one drawcall and if the object is a little complex like a plant I can see through the object, you can see that we can see leaves on the top of the big one, but they are normally behind.

My draw function code:

glDepthFunc(GL_LESS);
glEnable(GL_CULL_FACE);
glFrontFace(GL_CCW);
glEnable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDepthMask(GL_FALSE);

glBindVertexArray(subMesh.VAO);
glDrawElements(GL_TRIANGLES, subMesh.index_count, GL_UNSIGNED_SHORT, 0);

Does anyone know what's the problem? My engine supports fixed pipeline and shaders, I would love a solution that works on both techniques.

Transparent objet

With glDepthMask set to GL_TRUE

enter image description here

This is the rendering I want

Rendering in Unity

Looks like in Unity there is the same issue when the rendering mode is set to Fade and Transparent, but I don't have the problem when I set it to Cutout. Maybe making a cutout system is the solution?

I tried to scale the object, reduce the far plane, so I don't think it's a depth precision issue.

I tried to use glDepthFunc(GL_LEQUAL); too.


Solution

  • First of all, the goal is to render a specific type of transparent object: one where the parts are either fully transparent or fully opaque. In Unity, one would use a Transparent Cutout Shader to render such objects.

    To achieve this, depth testing, including writing to the depth buffer, should be enabled. Therefore, use glDepthMask(GL_TRUE) (the default setting).

    From the screenshot in the question, we can now see that the problem is that the black parts should be fully transparent instead. There are two ways to solve this:

    1. Redesign the object, so that the geometry exactly matches the opaque parts.
    2. Use Alpha Testing.

    Alpha Testing

    To use Alpha Testing, the texture needs to be have an alpha component. Since the object renders as desired in Unity with the Transparent Cutout Shader, we may assume that it has. How to implement it, depends on the API:

    1. Legacy OpenGL ("fixed pipeline")
      Use the following code, which keeps fragments with an alpha value greater than 0.5 and discards others.

      glAlphaFunc(GL_GREATER, 0.5);
      glEnable(GL_ALPHA_TEST);
      
    2. Modern OpenGL ("using shaders")
      This is a manual implementation of the above, in the fragment shader:

      if (texel.a <= 0.5)    // Note: the documentation uses <, which is not exactly equivalent.
          discard;
      

      LearnOpenGL has a good tutorial on this with detailed explanations.


    Additional info: If the goal were to render a semi-transparent object, as in a plant made of stained glass, a completely different approach is needed. The simplest being, first depth-sorting the individual primitives (e.g. triangles) followed by rendering these one at a time, back-to-front (ref). This is computationally expensive and may not produce 100% correct results. Other, more accurate, approaches exist in the field of Order-Independent Transparency.