In OpenGL, DEPTH-CUING and fog effects are controlled by fog parameters. A fog factor is computed according to one of three equations, and this fog factor performs a linear blend between the fog color and the computed color for the fragment. The depth value to be used in the fog equation can be either the fog coordinate passed in as a standard vertex attribute (gl_FogCoord) or the eye-coordinate distance from the eye. In the latter case, it is usually sufficient to approximate the depth value as the absolute value of the z-coordinate in eye space (i.e., abs(ecPosition.z)). When there is a wide angle of view, this approximation may cause a noticeable artifact (too little fog) near the edges. If this is the case, you could compute z as the true distance from the eye to the fragment with length(ecPosition). (This method involves a square root computation, so the code may run slower as a result.) The choice of which depth value to use would normally be done in the vertex shader as follows:
if (UseFogCoordinate) gl_FogFragCoord = gl_FogCoord; else gl_FogFragCoord = abs(ecPosition.z);
A linear computation (which corresponds to the traditional computer graphics operation of depth-cuing) can be selected in OpenGL with the symbolic constant GL_LINEAR. For this case, the fog factor f is computed with the following equation:
start, end, and z are all distances in eye coordinates. start is the distance to the start of the fog effect, end is the distance to the end of the effect, and z is the value stored in gl_FogFragCoord. We can explicitly provide the start and end positions as uniform variables, or we can access the current values in OpenGL state by using the built-in variables gl_Fog.start and gl_Fog.end. The shader code to compute the fog factor with the built-in variables for accessing OpenGL state is shown in Listing 9.15.
Listing 9.15. GL_LINEAR fog computation
Because 1.0 / (gl_Fog.end gl_Fog.start) doesn't depend on any per-vertex or per-fragment state, this value is precomputed and made available as the built-in variable gl_Fog.scale.
We can achieve a more realistic fog effect with an exponential function. With a negative exponent value, the exponential function will model the diminishing of the original color as a function of distance. A simple exponential fog function can be selected in OpenGL with the symbolic constant GL_EXP. The formula corresponding to this fog function is
f = e(density .z)
The z value is computed as described for the previous function, and density is a value that represents the density of the fog. density can be provided as a uniform variable, or the built-in variable gl_Fog.density can be used to obtain the current value from OpenGL state. The larger this value becomes, the "thicker" the fog becomes. For this function to work as intended, density must be greater than or equal to 0.
The OpenGL Shading Language has a built-in exp (base e) function that we can use to perform this calculation. Our OpenGL shader code to compute the preceding equation is shown in Listing 9.16.
Listing 9.16. GL_EXP fog computation
The final fog function defined by OpenGL is selected with the symbolic constant GL_EXP2 and is defined as
f = e(density .z)2
This function changes the slope of the exponential decay function by squaring the exponent. The OpenGL shader code to implement it is similar to the previous function (see Listing 9.17).
Listing 9.17. GL_EXP2 fog computation
OpenGL also requires the final value for the fog factor to be limited to the range [0,1]. We can accomplish this with the statement in Listing 9.18.
Listing 9.18. Clamping the fog factor
Any of these three fog functions can be computed in either a vertex shader or a fragment shader. Unless you have very large polygons in your scene, you probably won't see any difference if the fog factor is computed in the vertex shader and passed to the fragment shader as a varying variable. This will probably also give you better performance overall, so it's generally the preferred approach. In the fragment shader, when the (almost) final color is computed, the fog factor can be used to compute a linear blend between the fog color and the (almost) final fragment color. The OpenGL shader code in Listing 9.19 does the trick by using the fog color saved as part of current OpenGL state.
Listing 9.19. Applying fog to compute final color value
The code presented in this section achieves the same results as OpenGL's fixed functionality. But with programmability, you are free to use a completely different approach to compute fog effects.