Inconsistent blending depending on cam angle


#1

Hello! I’m stuck with a blending issue, hope someone can give me a clue!

I’m trying to render a simulation of row of arches with light beams coming out of them. For the light beams I just render cylinder-shaped meshes. The problem is that the blending seems inconsistent based on the angle from which the camera points at the structure.

In the below image #1 (actual image of the current state of my app) the top left view is pretty close to what I want, but the top right view (where the camera faces the structure from the other direction) you can see the blending issue I’m struggling with (transparent parts of the light beams seem to be blocking the light beams behind them).

image #1:

In the first image (above) all light beams are rendered as one big single BatchRef (generated from my custom geom::Source class).

In the below images each “arch” of beams is a separate BatchRef, and the meshes are generated by cinder’s geom::Cone Source class (I’m trying to isolate the problem by not using my custom geom class and using separate batches for rendering).

In the below images #2 and #3 I’m using the normals as color and setting the alpha to a constant 0.5 value in the fragment shader.

In #2 the cones seem almost solid, only showing transparency with the gray structure (separate mesh, rendered first) and a little bit with some of the beams from its own mesh, but NOT with the beams in the meshes behind them.

image #2

image #3 (same scene as #2 but camera again from the other side); this is closer to what I’d want, with more transparency all around.

Finally for good measure two images similar to #2 and #3, but with a single constant color, set in the fragment shader;

image #4 (like #2 but with solid color and again 0.5 alpha);

image #5 (like #3 but with solid color and again 0.5 alpha);

Finally here is the render method of a single group of beams.
Note that;

  • all light-beam batches are rendered immediately after each other
  • all rendering goes into an fbo, which is then rendered to the screen (I do this for the split-screen as seen in image #1)
  • depth testing is enabled
  • I’ve tried different blending options, but in image #2 to #5 they were always
    “GL_SRC_ALPHA” (source) and GL_ONE_MINUS_SRC_ALPHA (dest).
void BeamGroup::draw() const {
    ci::gl::ScopedDepth scpDepth(bDepthTestEnabled);
    ci::gl::ScopedBlend scpBlend(mBlendSrc, mBlendDest);

    ci::gl::pushMatrices();
    ci::gl::scale(ci::vec3(3.0f));
    mTexture->bind();
    mBatch->draw();
    ci::gl::popMatrices();
  }
}

Here is my fragment shader as used for images #4 and #5;

 #version 150
 
 in vec4		Color;
 in vec3		Normal;
 out vec4 	oColor;
 
 void main( void )
 {
oColor = vec4(1, 0.5, 0.2, 0.5);
}

Hope this makes sense. All suggestions are appreciated!


#3

Hi shortnotion.

You have come across a very common ‘problem’ with alpha blending which is that to achieve correct transparency rendered objects must be sorted back to front (in terms of distance to the camera). If you are rendering seperate cones in a forloop then naturally the cones will render in that order, if you are rendering all the cones as a joined mesh then the traingles will be drawn in the order they are created (by the geom::Source by the sounds of it). This is why the scene looks good from one side but not the other.

You can read all about this here or googling ‘transparecy sorting’.

In the mean time I think you might have some luck with additive blending as it doesnt require sorting and achieves a glowing effect. try:

{
    gl::ScopedBlendAlpha alphaScp;
    gl::ScopedDepth readWriteScp(true);    // makes arches solid
    // Draw arches here
}
{
    gl::ScopedBlendAdditive addScp;
    gl::ScopedDepthTest testScp(true);        // occluded by arches
    // Draw lights here
}

Hope this helps.


#4

To elaborate on @felixfaire’s post, here’s a few things you could try:

  • Use additive blending. Additive blending is order independent, so you don’t have to sort your transparent geometry. As explained by Felix, render your opaque geometry first (with depth read and write enabled), then enable additive blending, disable depth writes, enable depth reads and render your transparent geometry. You may have to reduce the color/brightness of your transparent geometry to get the effect you’re after.
  • If all of your transparent geometry uses the same color and alpha, you don’t necessarily have to sort it. Just make sure to render your opaque geometry first, then enable alpha blending, disable depth writes, enable depth reads and render your transparent geometry. You may still see some visual glitches caused by anti-aliasing or draw order.
  • You may see good enough results if you sort your transparent geometry and draw them back to front. A simple approach might be to sort them based on their distance from the camera position. For even better results, first draw all back-facing polygons (enable front face culling), then draw all front-facing polygons (enable back-face culling). See this sample for a very basic implementation.
  • If you still need a better solution, you will have to properly sort your geometry, look into depth peeling or something like this.

-Paul


#5

YES, thanks so much guys, disabling depth write for the light beams did the trick! I tried additive blending (and various other blend-modes) but with depth-write enabled the results were still weird. Makes complete sense in retrospect.