laurGanta
laurGanta

Reputation: 185

OpenGL - How to create Order Independent transparency?

I've been working on a game engine for educational purposes and I came across this issue I cannot seem to find an answer for:

Alpha channel only works for objects that have already been drawn before the object that has the alpha channel (For example: in a scene with 3 objects, let's say a cat, a dog and a bottle(transparent). both the cat and the dog are behind the bottle; the dog is drawn first, the bottle second, the cat third. only the dog will be seen through the bottle).

Here's a picture of this issue: The objects are drawn in the order they appear in the list box

I used C++ for the engine, Win32 API for the editor and GLSL for shading:

// some code here
vec4 alpha = texture2D(diffuse, texCoord0).aaaa;
vec4 negalpha = alpha * vec4(-1,-1,-1,1) + vec4(1,1,1,0);

vec4 textureComponentAlpha = alpha*textureComponent+negalpha*vec4(1,1,1,0);//(texture2D ( diffuse, texCoord0 ) ).aaaa;

gl_FragColor = (textureComponentAlpha + vec4(additiveComponent.xyz, 0)) * vec4(lightingComponent.xyz, 1);

In C++:

glEnable(GL_ALPHA_TEST);
glDepthFunc(GL_EQUAL);

glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

I assume it has something to do with the way the alpha test is made, or something like that.

Could anyone help me fix this, please?

Upvotes: 15

Views: 13256

Answers (4)

Fedor
Fedor

Reputation: 21291

The method to get correct transparency of rendered objects independently from the order in which they are drawn is named Order Independent Transparency (OIT).

There is a great presentation from Nvidia summarizing the latest solutions in this area: Order Independent Transparency In OpenGL 4.x

"OpenGL 4.x" in the title is not accidental, because only in OpenGL 4.2 core appears Atomic Counters, which are important for OIT implementation.

One of the algorithms of OIT is as follows:

  1. During the first pass of rendering, store each fragment in a buffer, and collect all fragments for a single screen pixel in a linked list. Atomic Counters are used both to store new fragments in a buffer and to maintain linked list in each screen pixel.
  2. During the second pass of rendering, each linked list is sorted according to z-depth and fragments are alpha-blended in correct order.

A simple alternative to OIT is to discard every second (odd) fragment in a fragment shader:

    if (onlyOddFragments && ((int(gl_FragCoord.x) + int(gl_FragCoord.y)) % 2) == 1)
      discard;

So you will see the objects farther from the camera in the discarded fragments. If multisample antialiasing (MSAA) is activated, then no checkboard pattern is visible even in lowest resolution.

Here is a video comparing the standard transparency approach where all triangles are output simply in order, as well as two above approaches. The implementation can be found in some open-source GitHub projects, e.g. here.

Upvotes: 2

Nebulous39
Nebulous39

Reputation: 51

Im not entirely sure this will help your situation, but do you have blending and alpha enabled? As in :

glEnable(GL_BLEND); 
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

Upvotes: 2

cmourglia
cmourglia

Reputation: 2524

I cannot encourage you enough to have a look at this NVidia paper and the related blog post by Morgan McGuire.

This is pretty easy to implement and has great results overall.

Upvotes: 9

Spektre
Spektre

Reputation: 51845

I am using something similar to that answer linked by @RetoKoradi comment but I got double layered transparent models with textures (glass with both inner and outer surface) with fully solid machinery and stuff around.

For such scenes I am using also multi pass approach and the Z-sorting is done by sequence of setting front face.

  1. render all solid objects
  2. render all transparent objects

    This is the tricky part first I set

    glGetIntegerv(GL_DEPTH_FUNC,&depth_funct);
    glDepthFunc(GL_ALWAYS);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_CULL_FACE);
    

    I got the geometry layers stored separately (inner outer) so The Z-sorting is done like this:

    • Render outer layer back faces with glFrontFace(GL_CW);
    • Render inner layer back faces with glFrontFace(GL_CW);
    • Render inner layer front faces with glFrontFace(GL_CCW);
    • Render outer layer front faces with glFrontFace(GL_CCW);

    And lastly restore

    glDisable(GL_BLEND);
    glDepthFunc(depth_funct);
    
  3. render all solid objects again

It is far from perfect but enough for my purposes it looks like this:

example

Upvotes: 21

Related Questions