In a continuing effort to educate the readers of the complexities of 3D rendering, nV News is happy to bring you Marlin Rowley to give a tutorial on Interpolative shading. This will be a multi-part series with additional pieces to be added soon, along with quick OpenGL demonstration programs.
Interpolative Shading means that during the rendering process of a polygon, given specific information about a vertex (whether it be color intensity, or a normal) of that polygon, calculations are made between these vertices using a linear equation(s) to find the information about the pixel inside a polygon (or in-between a polygon edge).
Common Techniques: Gouraud Shading
Gouraud is the most popular interpolative shading technique today. The main reason why is because it is so fast and very easy to implement. Most hardware accelerated graphic cards today use this form of shading since the APIs (Application Programming Interface) available (Direct3d and OpenGL) only provide 2 forms of shading: Flat and Gouraud. While Gouraud is the common technique in a realtime renderer, it cannot produce accurate highlights. This is just one of the problems implementing this technique.
Gouraud calculates intensities at the polygon vertices only, using a local reflection model, and interpolates these pixels within the polygon. What does "local reflection model" mean? This means that the Gouraud shading algorithm gets intensities by getting the normal at each vertex and other pertinent information (i.e. vector to the light source) and uses it in a light equation to calculate the vertex color. Lets observe the algorithm whereby a polygon gets filled during the rendering process using Gouraud Shading:
- Send the vertex to the hardware (or CPU) to be transformed
- Compute the normal at that vertex (usually by the CPU or pre-calculated)
- Apply the local reflection model at that vertex to get the color intensity
- Render the scan line
- Fill the triangle
- Interpolate the intensities from two pair of vertices by using a linear equation.
This is typically how OpenGL and Direct3D work in conjunction with a hardware accelerated graphics card. Most of this is done in hardware, and so it is very efficient and fast.
Lets first look a little closer at the vertex normals. One may ask, "Are these the actual normals of the surface?" The answer is "No". Remember that the surface is being approximated by polygons so the vertex normals are also an approximation. So how do we approximate the normals? The answer is normal averaging.
Here in Figure 1, we see that the normal Na is the average of: N1, N2, N3 and N4. I purposely made triangles from the quads to give an illustration of a triangulated surface. Notice if you were examining the normal at the edge. You would only have 2 normals to average at most. This normal averaging can be easily integrated into hardware.
It is important to note that this is an approximation technique (Numerical Analysis), thus there will be some errors the further you are away from the "true" normal. So how do we get close to the true normal of the surface? By increasing the number of triangles to approximate more of the "true" surface.
Now observe the rendering phase of the pipeline where the polygon gets filled by the intensity of the vertices.
In Figure 2, you see that intensities along an edge are interpolated from pairs of vertex intensities, which produces a start - finish intensity for each scan line. The formula for interpolation of this polygon is:
Let's apply a realistic example into the formula so that we don't just see variables and not really understand what is going on. Suppose we had just started scan line 75. Let's fill some variables in to see what the intensity is at Is.
Let Y1 = 10, Y2 = 250, Ys = 75, I1 = 1.0, and I2 = 0. By plugging these values in for the formula Ia, you'll get Ia = .7292
Now for Ib, let Y4 = 250 and I4 = .67, we get Ib = .7617
Using the last formula, we can easily get Xa and Xb by using a similar interpolative technique for scan lines (another topic), but for the sake of clarity, let X1 = 150, X2 = 25 respectively, so that we have Xa = 100. Let X4 = 250, so that Xb = 200 and finally, Xs = (Xb - Xa) = 100. Now all we have to do is plug in all the values to obtain an interpolated intensity for Is = 0.7292.
- Easy to implement in hardware
- Works best with diffuse component of local reflection models
- Not good with specular highlights.
Notice if there were a specular highlight in the middle of a quad (or any polygon), Gouraud shading would miss it completely since it only has information about the intensities of the pixel at the vertices of the polygon.
- Produces "Mach" Bands
Mach Bands are due to the eye noticing discontinuities from polygon to polygon that shares vertices. It comes across as a light line. Mathematically explained, it is because the derivative is zero going from one polygon to the next (on shared or adjacent polys).
This leads us into the next interpolative shading algorithm - Phong Shading. Coming Soon...
Marlin Rowley is a programmer in the R&D department at Blue Sky Studios. nV News would like to thank Marlin for taking time to contribute to the site in an effort help readers fully comprehend the complexities of modern 3D redering.
Download the tutorial's demo by clicking here