Gouraud Shading

Gouraud Shading


Gouraud shading is a method for linearly interpolating a colour or shade across a polygon. It was invented by Gouraud in 1971. It is a very simple and effective method of adding a curved feel to a polygon that would otherwise appear flat. It can also be used to depth que a scene, giving the appearance of objects in the distance becoming obscured by mist. I suggest you also read the page on light sourcing if you do not already know the principles.



On the left is a curved surface rendered using flat shaded polygons, the other is Gouraud shaded. It is quite clearly smoother and more attractive and well worth spending the time to code.

Unlike a flat shaded polygon, you can specfy a different shade for each vertex of a polygon, the rendering engine then smoothly interpolates the shade across the surface. The technique is very similar to the standard scan converting, and can be handled very quickly with integer maths.

Some points to note though: It is best to restrict gouraud shading to three sided polygons. Sometimes polygons may not look quite as you expect when they have more than three vertices with very different shades.


Scan converting

The scan converting algorithm is very similar to the flat shading algorithm, so I won't go into very much detail. In this case, you not only calculate the change in X value down the scanline, you also keep track of the shade. You do this in exactly the same way.

Now, before the horizontal strip is rendered to the screen, some small adjustments need to be made. It will help to have a picture here to clarify what's going on.

OK, the tiny polygon shown here is flat shaded just for clarity. Consider the line to be rendered, A-B. If this were a perfect world, it would be possible to render the line A-B. However, this is a pixelated screen, so it's only possible to render the line of three pixels C-D. The center of the first pixel, C, does not lie exactly on the point A, and pixel D does not lie exactly on the point B, so a slight adjustment is made for the sake of increased accuracy. Now, you may think this is just being pedantic, however, taking the trouble to increase the accuracy results in a much more beautiful polygon. This is especially true when you add texture mapping etc and the polygons are quite small.

So, how to make this adjustment. Firstly calculate the gradient of shade across the line as usual:

Gradient = (Bs - As) / (Bx - Ax)

Where As = shade at A, and Ax is the X value of A. Now calculate the exact value of the shade at C:

Cs = As + (1-frac(Ax))*Gradient


So, now you need to be able to render a strip of Gouraud polygon to the screen. This involves calculating the shade of each pixel and writing it to the screen. This is a simple process, since the shade changes linearly across the scanline. The process can be demonsrtated by a little pseudocode.

Shade = Cs
loop X from Cx to Dx
	plot pixel at (X,Y) with colour Shade
	Shade = Shade + Gradient
End of X loop
As usual, it's possible to reall crank up the speed of this algorithm by writing it in machine code. The algorithm given below will only really work on a PC in an 8-bit screen mode, but of course there are other methods for other platforms. Please note that this method is not perfect and will not produce perfect results where the rate of change of shade across a polygon is very small. But it is fine for most cases and faster than a Breshenham type mothod.

This method relies on the fact that the x86 series chips have 16 bit registers that can be treated as two 8 bit registers. In this implementation, the highest 8 bits of a regester are used as the integer part of the shade being interpolated, and the lower 8 bits are used as the fractional part.

This inner loop given just renders one horizontal strip of a polygon in an 8 bit screen mode. It uses 32 bit memory pointers. Because of the fact that it uses a fixed point value for the shade, it is possible to assign non-integer values for the shade values of the vertices, making for slightly more accurate rendering.

The routine requires a little setup code to begin with:

length = Dx - Cx + 1
ScreenPtr = y*ScreenWidth + Cx
Now the inner loop:
CX = length
AX = Cs * 256
BX = Gradient
EDI = ScreenPtr

top:
	mov	[edi], ah		;write the pixel to the screen
	inc	edi			;next pixel
	add	ax, bx			;update shade
	dec	cx			;decrement counter
	jnz	top			;loop if more pixels


Notes on Gouraud Shading

Gouraud shading is by no means perfect, but it can make a real difference over flat shaded polygons. Problems with Gouraud shading occur when you try to mix light sourcing calculations with big polygons.

Imagine you have a large polygon, lit by a light near it's center. The light intensity at each vertex will be quite low, because they are far from the light. The polygon will be rendered quite dark, but this is wrong, because it's centre should be brightly lit. You can see this happening in the game Descent. Firing flares around, especially into corners, causes the surroundings to light up. But try firing a flare into the middle of a large flat wall or floor, and you will see that it has very little effect.
However, if you are using a large number of small polygons, with a relatively distant light source, Gouraud shading can look quite acceptable. Infact, the smaller the polygons, the closer it comes to Phong shading.