5

Explain me what i'm doing wrong. I have loaded 3d model to self-coded opengl render (v 3.3) and try to make it transparent like xray effect using vertex shader:

#version 330

attribute vec3 coord3d;
attribute vec2 texcoord;
varying vec2 f_texcoord;

uniform mat4 projectionMatrix; 
uniform mat4 modelViewMatrix; 

layout (location = 0) in vec3 inPosition;
layout (location = 1) in vec4 inColor;

smooth out vec4 theColor;

void main()
{
    gl_Position = projectionMatrix*modelViewMatrix*vec4(inPosition, 1.0);
    theColor = vec4(0.0,0.2,0.4,0.4);
    f_texcoord = texcoord;

}

The model was triangulated in editor and draw with:

glDrawArrays(GL_TRIANGLE_FAN, 0, (vertices.at(i)->size()/3));

If i use

glEnable(GL_ALPHA_TEST);
glEnable( GL_BLEND );
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
glEnable(GL_DEPTH_TEST);
glClearDepth(1.0); 

I see some unwanted triangles or lines:

If i do it without depth test i see multiple triangless inside faces (which i don't want):

How can i rid of unwanted effects and implement x-ray effect like Google Sketchup did it

Should i implement depth sorting if i want all model transparent?

How can i implement this:

GS

xcix
  • 83
  • 1
  • 8
  • 3
    I can reproduce a similar case in blender with adjacent cubes and an alpha of 0.4. The simple answer is: z-fighting. Removing one of the overlapping faces fixes the problem. – thokra Sep 19 '13 at 13:14
  • 1
    On another note, why the fixed-function alpha test? Why not use fragment-shader discard? In general, do you need compatibility or are you just too lazy to do it manually? ;) – thokra Sep 19 '13 at 13:20
  • +1 for @thokra you can at minima play with your near & far values. – johan d Sep 19 '13 at 15:07
  • If i use fixed color set like vec4(0.0, 0.2, 0.4, 0.4) should i perform alpha test in shader? – xcix Sep 20 '13 at 09:00
  • @xcix: The question is not if you need to, but if you want to. If you want to discard stuff based on an alpha value, you can do it in the fragment shader. You get the same results as when using the `ALPHA_TEST`. The difference is, fixed-function alpha testing has been removed with GL 3.1, so it's legacy OpenGL. Fragment shader discard is a core GLSL 440 feature. – thokra Sep 20 '13 at 10:10
  • @thokra: I mean that shoud i fundamentally use alpha test (its not care where - in c++ code or shader), when alpha-component is constant? – xcix Sep 20 '13 at 10:25
  • @xcix: No, just because you have a constant alpha value doesn't mean you should use alpha testing. If you don't want fragments to be discarded based on an alpha value and the corresponding alpha function, simply don't use alpha testing. Alpha testing is useful, for instance, for so called alpha cut-outs, where you have regions of a rectangular texture that are completely blank and have an alpha value of 0. – thokra Sep 20 '13 at 10:33

2 Answers2

9

Problem 1:

First of all, don't disable the depth test. The depth test is performed before blending to ensure fragments are correctly dropped, even when blending is enabled. For more information on per-fragment operations and the order in which they are performed, see section 17.3 of the core OpenGL 4.4 specification.

When you disable depth testing, you will get unexpected results unless the order of draw calls is absolutely correct, thus defying the very purpose of depth buffering. And even if you get the order right you cannot expect all side effects to be gone and trying to order every command just the way you need it doesn't work for anything but the simplest programs.

Consider the following, very simple example:

enter image description here

You can see 3 quads, the farthest visualizing the usual tex coordinate space (s,t E [0, 1]), a blue quad which blends with the latter, and an opaque red quad which is supposed to be in the front. Obviously, the red quad isn't in front, although the depth values indicate otherwise. Also obvious, the intent is to not blend the blue quad with the red quad. Rendering the red quad after the blue quad seemingly fixes the problem, but if you draw another quad which is supposed to be behind both the blue and the red quad, it would simply appear on top with disabled depth testing. This is all wrong.

enter image description here

The yellow quad obscures everything, although it's supposed to go behind the red and blue quad. The correct image is the following, which is obtained purely by enabling depth testing:

enter image description here

My general suggestion would be: unless you have a very good reason to disable the depth test, for instance when rendering overlays and not wanting to hassle with the depth buffer, leave depth testing enabled.

Note: Depth buffering will not solve your problem of usually having to arrange non-opaque geometry back-to-front to get a correct result! If you want to be completely independent of ordering when trying to simulate transparency, look for the usual suspects among the many papers on order independent transparency.

Problem 2:

From the looks of it, I assume your using the following blend function:

gl::BlendFunc (gl::ONE, gl::ONE);

or anythin that leads to a steady, successive accumulation of values like

gl::BlendFunc (gl::SRC_ALPHA, gl::ONE);

and the default blend-equation for RGB and alpha components:

gl::BlendEquation (gl::FUNC_ADD);

If this isn't the case, please add a comment stating the actual values so I can re-recheck and possibly edit my advice. The following still applies because colors just don't lie. ;)

Your second problem is due to z-fighting under projection. Some fragments are correctly discarded, others aren't. Since you obviously have duplicate faces that exactly coincide in some places, it may be possible that for one fragment a slightly higher depth value is generated for the first face. When the second face is rendered, a slightly lower depth value is generated and the fragment passes, leading to overdraw where there should actually be none.

The first fragment has already done all the magic and a blended value was written into the frame buffer and the depth value was written to the depth buffer, i.e. you have one fragment of the face that passed all tests and has blended with the background. Now comes the second fragment which is not discarded because the depth value is slightly lower and again blends with the already blended color in the framebuffer. Due to additive blending, you get the results you observed. I reproduced this case in the following example with the same color and alpha values you used:

enter image description here

Problem 3: Your model seems to have a lot of duplicate faces and in general, the triangulation looks horrible. You should really redo it. Understanding and using blending is hard enough as it is. Don't complicate it with sub-optimal data.

thokra
  • 2,874
  • 18
  • 25
  • I use: `code` glBlendFunc(GL_SRC_ALPHA, GL_ONE); glBlendEquation (GL_FUNC_ADD) trying to implement x-ray effect. If you now right way to make all objects half-transparent (there are no opaque objects on the scene), let me know about this. – xcix Sep 20 '13 at 10:01
  • @xcix: Yeah, tried that too. ;) I chose `ONE, ONE` to better visualize the accumulation. The only difference is: `finalColor = SRC_ALPHA * source + destination` instead of `finalColor = source + destination`. Updated my answer accordingly. – thokra Sep 20 '13 at 10:05
0

The typical procedure for rendering transparent objects is as follows:

  1. Draw Opaque objects
  2. Set depth mask to false: glDepthMask(GL_FALSE);
  3. Sort transparent triangles back to front.
  4. Render transparent in this order.
  5. Set depth mask to true: glDepthMask(GL_TRUE);

This is the only way you can be sure that you will get accurate results. Unfortunately, depth sorting is both complicated and time consuming. Sometimes, you can get away with skipping it and get results that are "good enough." There are also techniques for order independent transparency, that can provide reasonably good results.

In your case it looks like depth sorting will be unavoidable though.

Edit: When drawing transparent objects, you can't leave depth testing fully enabled, because then pixels behind anything what you draw would be overwritten. You also can't disable it entirely either, because then you would be able to see transparent objects even if they were behind opaque ones. The solution is to still check whether the transparent fragments you draw are occluded by closer (opaque) ones, but not to save depths of the transparent fragments so they aren't checked against later. The method used to tell OpenGL to stop/start saving depth values is glDepthMask().

fintelia
  • 1,201
  • 6
  • 17
  • Can you elaborate why you need to mask depth writes? – thokra Sep 19 '13 at 20:41
  • I have no any opaque objects, all should be transparent, if i disable depth then i have an image like a second screenshot i've posted. – xcix Sep 20 '13 at 09:16
  • Is the any technique to sort triangles from back to front in std::vector of coords? I mean, i use: glDrawElements(GL_TRIANGLE_FAN,indices.size(),GL_UNSIGNED_INT,0); Maybe some std::algorithm? And how i can link it with array of indices? – xcix Sep 20 '13 at 11:17
  • And what is sorting criteria in set of triangles? z>z2? If i have two perpendicular triangles, how can i sort them properly? – xcix Sep 20 '13 at 11:26
  • If you have triangles that intersect each other, you may have to split them so they are rendered in the right order. Depth sorting is a complex topic. You can look online or ask a separate question if you have more questions about it. – fintelia Sep 20 '13 at 14:22
  • Please, correct me but if i split triangles, which forms faces (quads or more complex polygons) i damage my model. Is it true way to search middle point inside a triangle and calculate distance between this point and camera point (which defined in gluLookAt) and sort by this distance? – xcix Sep 20 '13 at 14:52
  • You could sort triangles by their centers, although it would produce incorrect results in certain situations. This explains some of the difficulties of depth sorting: https://www.opengl.org/wiki/Transparency_Sorting – fintelia Sep 20 '13 at 15:00