Rendering Scenes With Translucent Objects:
A (Nearly) Pure GPU Approach
render Methods in SceneElement subclasses
The bulk of the work in this approach is done in the fragment shader which only needs to know whether opaque
or translucent pieces are currently being drawn. So the minimal amount of work needed in this approach is to simply
set a uniform variable that the fragment shader will see. This code could be placed, for example,
in SceneElement::establishView:
ExtendedController* ec = dynamic_cast<ExtendedController*>(Controller::getCurrentController());
glUniform1i(shaderIF->ppuLoc("sceneHasTranslucentObjects"), 1);
if (ec->drawingOpaque())
glUniform1i(shaderIF->ppuLoc("drawingOpaqueObjects"), 1);
else
glUniform1i(shaderIF->ppuLoc("drawingOpaqueObjects"), 0);
The GPU fragment shader work
The following shows the rough outline of a fragment shader that can be used with this
approach.
…
uniform int sceneHasTranslucentObjects = 0;
uniform int drawingOpaqueObjects = 1;
out vec4 fragColor;
…
void main()
{
// computeColorAsUsual must:
// 1. combine Phong color with texture color;
// 2. set color.a = (texture has alpha) ? f(PPU alpha, texture alpha) : PPU alpha
vec4 color = computeColorAsUsual();
if (sceneHasTranslucentObjects == 1)
{
if (drawingOpaqueObjects == 1)
if (color.a < 1.0)
discard;
else
fragColor = color;
else if (color.a < 1)
fragColor = color;
else
discard;
}
else
fragColor = color;
}
Advantages
- Compute and store in frame buffer the translucency of objects on a pixel-by-pixel basis for each object, hence:
- Less CPU code clutter since you don't have to sprinkle translucency-related
tests all over. Note in particular that ModelView (or SceneElement) subclasses have
no need to even know whether translucent or opaque objects are currently being drawn (other than
the small segment of code shown at the top of the page).
- Easier for complex model structure in which parts of an object are translucent and
other parts are opaque
- The translucency of objects may be computed in the graphics pipeline before the
fragment shader starts (i.e., in the vertex, tessellation, and/or geometry shaders). Or translucency
may be literally determined pixel by pixel in the fragment shader itself – e.g., procedurally
and/or in conjunction with a texture map containing an alpha channel – hence a purely
CPU side approach would be extremely difficult, and perhaps not even possible.
- The text rendering package we use will work more easily with this approach.
- It is simple to optimize the implementation by immediately exiting calls to
render methods during an opaque drawing pass if they draw only translucent objects (and symmetrically
for translucent passes and purely opaque objects). This could go a long way towards
eliminating most of the first disadvantage cited below.
Disadvantages
- Doubles the time required to perform the display callback. (But this will only be noticeable for scenes
containing an extremely large number of vertices.)
- It is more difficult to incorporate depth sorting of translucent polygons, should you want to do that.