I am an intermediate OpenGL programmer. I’ve been doing basic OpenGL stuff for years, so I’m not a newbie, but I’m also not familiar with all the advanced techniques that are out there.
One problem that has always come up over the years is efficiently and correctly drawing multiple transparent, complex objects on the screen (mixed with opaque objects as well). These are objects that move around relative to eachother, sometimes overlapping, typically with a user controlling the viewpoint location as well. This is a pretty classic problem.
The current application I am writing is really frustrating me. This particular application uses OpenGL to draw 3 complex objects on the screen:
[ol][li]An opaque, convex surface model consisting of approximately 2000 triangles.[]A transparent, extremely complex, definitely-not-convex (and possibly not even fully closed), surface model, requiring no culling, two-sided lighting, and consisting of anywhere from 40000 to 200000 triangles.[]A transparent, textured quad with an animated texture on it, requring no culling and two-sided lighting.[/ol][/li]
In this application, all 3 objects are constantly moving around relative to each other. Particularly, the objects can overlap or be inside each other. The user can adjust the camera position and orientation and will do so frequently. The view may be from inside surface #2 looking out. Two things are critical:
[ul][li]High frame rates at all times (>= 30 FPS).[*]Correct (i.e. relatively realistic, though not necessarily perfect) appearance of transparency, even when objects overlap. For example, if textured quad #3 is partially inside surface model #2, it must look correct. Note that a single triangle in quad #3 may very likely be partially contained in the surface model, so even sorting triangles back-to-front would not handle the case correctly.[/ul][/li]
I feel I’m faced with a very common problem. Ideally, I’d want to be able to render all fragments from back to front, applying the blend function (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) (or whatever blend function, but that one is the kind of results I’m looking for). I can sort the objects back-to-front but the scene is too complex to get satisfactory results from this, and it does not handle self-transparency or overlapping objects either. I can sort all triangles from back to front but this does not handle overlap correctly either, and also it is far too slow to be useful (the best implementation I know how to do is quicksort on triangles based on their centroid’s GL window Z coordinate – multiplying all centroids by current matrix stack by hand first, even using optimized math libraries like GSL this is just too slow as triangle counts get into the 200000’s and up).
It’s definitely a priority for me to get it working for the particular case in my current application, but I’d really like to add some tips and techniques for general case transparency issues to my belt.
So far I’ve mostly just always tried a lot of depth buffer and blend function “tricks”, none producing completely satisfactory results. Even the simple technique of turning off depth buffer writes doesn’t produce good results with non-commutative blend functions like (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA). As an example of how weird I’ve been getting here is the last thing I tried, where s_BigThing.Render() draws a complex non-convex surface model:
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
glColor4f(1, 1, 0, s_Alpha);
glDisable(GL_CULL_FACE);
// fill depth buffer with nearest values
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glDepthFunc(GL_LESS);
s_BigThing.Render();
// draw only the farthest faces
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glDepthFunc(GL_GREATER);
glBlendFunc(GL_SRC_ALPHA, GL_ZERO);
s_BigThing.Render();
// refill depth buffer with nearest value
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glDepthFunc(GL_LESS);
s_BigThing.Render();
// draw only the nearest faces, showing far faces through them.
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glDepthFunc(GL_LEQUAL);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
s_BigThing.Render();
That is ridiculous, of course. Four-pass rendering is not acceptable, although performance is still OK it doesn’t seem to make sense, and that doesn’t deal with intersecting objects either, obscured objects, and doesn’t quite look right anyway (many faces are missing when the object obscures itself multiple times). Still it’s the closest I’ve gotten. I’ve gotten OK results using clipping planes to render ranges of the scene from farthest to nearest, but this requires many passes and still looks pretty ugly unless you have a lot of “depth sections”. Typically when I try to come up with a new solution I get one of the same types of problems in the result:
[ul][li]Too slow. E.g. sorting a half a million triangles, or rendering the scene a hundred times per frame.[]Triangle “flashing” problems as back-to-front drawing order changes when an object is moving (for example, rotating a surface model).[]Too much contrast, for example using (GL_SRC_ALPHA,GL_ONE) with depth writes turned off produces exceedingly bright areas where fragments overlap, even though it gets rid of that “flashing” effect.[]Non-commutative blend functions producing ugly results when polygons drawn out of order. In the worst case if you used (GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA) but draw polygons front-to-back, the farther away ones appear closer just because of the way the blending works (their fragment colors are weighted more in final result).[]Have yet to find any technique that produces even slightly satisfactory results for two overlapping objects (e.g. triangles are intersecting each other) when both are transparent.[/ul][/li]
I’m really looking for some good techniques that can help me do all of this. I’m willing to take any approaches, and to spend a good amount of time sitting down and learning stuff if necessary (I’m in no rush, I’d like to figure it out once and for all), the only requirements are the 2 listed above (performance + relative correctness).
Really what I want is a graphics card feature that stores all fragments in buckets, then sorts them all before blending and drawing, and does it really, really fast. Then I wouldn’t have to think about it at all.
Thanks a lot,
Jason