GLSL Programming/GLUT/Transparent Textures

This tutorial covers various common uses of alpha texture maps, i.e. RGBA texture images with an A (alpha) component that specifies the opacity of texels. 
It combines the shader code of the tutorial on textured spheres with concepts that were introduced in the tutorial on cutaways and the tutorial on transparency.
If you haven't read these tutorials, this would be a very good opportunity to read them.
Discarding Transparent Fragments.
Let's start with discarding fragments as explained in the tutorial on cutaways. Follow the steps described in the tutorial on textured spheres and assign the image to the left to the material of a sphere with the following fragment shader (keep the same vertex shader):
varying vec4 texCoords;
uniform sampler2D mytexture;
float cutoff = 0.1;
void main(void) {
 vec2 longitudeLatitude = vec2((atan(texCoords.y, texCoords.x) / 3.1415926 + 1.0) * 0.5,
 (asin(texCoords.z) / 3.1415926 + 0.5));
 gl_FragColor = texture2D(mytexture, longitudeLatitude);
 if (gl_FragColor.a &lt; cutoff)
 // alpha value less than user-specified threshold?
 discard; // yes: discard this fragment
If you start the application now, the fragment shader should read the RGBA texture and compare the alpha value against the threshold specified in the variable codice_1. If the alpha value is less than the threshold, the fragment is discarded and the surface appears transparent.
Since we can look through the transparent parts, it makes sense to not to enable backface culling as described in the tutorial on cutaways.
Alpha Testing.
OpenGL (non-ES) has a fixed-function feature, similar to the stencil buffer, to accept or discard a fragment based on its alpha value, through codice_3.
 glAlphaFunc(GL_GREATER, 0.1);
 glEnable(GL_ALPHA_TEST);
Blending.
The tutorial on transparency described how to render semitransparent objects with alpha blending.
Let's remind that proper transparency support requires sorting the triangles by distance to the camera, as we have to disable depth test to write "behind" a transparent triangle.
To avoid sorting triangles for a single spherical object, we use culling to draw the back faces first, and then the front faces.
So take an RGBA texture, the textured spheres shaders, and this OpenGL configuration:
glDisable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glCullFace(GL_FRONT);
glutSolidSphere(1.0,30,30);
glCullFace(GL_BACK);
glutSolidSphere(1.0,30,30);
It should be mentioned that this particular texture image contains only alpha values of either 0 or 1. Thus, there are relatively few fragments that receive an alpha value in between 0 and 1 due to interpolation of the alpha values of neighboring texels. It is only for those fragments that the order of rendering is important. If one accepts potential rendering artifacts for these fragments, one might improve the performance of the shader by enabling the depth test:
glEnable(GL_DEPTH_TEST);
Note that all texels with an alpha value of 0 are black in this particular texture image. In fact, the colors in this texture image are “premultiplied” with their alpha value. (Such colors are also called “opacity-weighted.”) Thus, for this particular image, we should actually specify the blend equation for premultiplied colors in order to avoid another multiplication of the colors with their alpha value in the blend equation. Therefore, an improvement of the shader (for this particular texture image) is to employ the following blend specification:
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
Blending with Customized Colors.
We should not end this tutorial without a somewhat more practical application of the presented techniques. To the left is an image of a globe with semitransparent blue oceans, which I found on Wikimedia Commons. There is some lighting (or silhouette enhancement) going on, which I didn't try to reproduce. Instead, I only tried to reproduce the basic idea of semitransparent oceans with the following shader, which ignores the RGB colors of the texture map and replaces them by specific colors based on the alpha value:
varying vec4 texCoords;
uniform sampler2D mytexture;
void main(void) {
 vec2 longitudeLatitude = vec2((atan(texCoords.y, texCoords.x) / 3.1415926 + 1.0) * 0.5,
 (asin(texCoords.z) / 3.1415926 + 0.5));
 gl_FragColor = texture2D(mytexture, longitudeLatitude);
 if (gl_FragColor.a &gt; 0.5) // opaque 
 gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0); // opaque green
 else // transparent 
 gl_FragColor = vec4(0.0, 0.0, 0.5, 0.7); // semitransparent dark blue
Of course, it would be interesting to add lighting and silhouette enhancement to this shader. One could also change the opaque, green color in order to take the texture color into account, e.g. with:
codice_4
which emphasizes the green component by multiplying it with formula_1 and dims the red and blue components by multiplying them with formula_2. However, this results in oversaturated green that is clamped to the maximum intensity. This can be avoided by halvening the difference of the green component to the maximum intensity 1. This difference is codice_5; half of it is codice_6 and the value corresponding to this reduced distance to the maximum intensity is: codice_7. Thus, in order to avoid oversaturation of green, we could use (instead of the opaque green color):
codice_8
In practice, one has to try various possibilities for such color transformations. To this end, the use of numeric shader properties (e.g. for the factors 0.5 in the line above) is particularly useful to interactively explore the possibilities.
Summary.
Congratulations! You have reached the end of this rather long tutorial. We have looked at:
Further Reading.
If you still want to know more