Vol. 2 Iss. 2 - May 2001

Gouraud Shaded Polys

By Sane <sane@telia.com>

Last issue you learned how to program flat-shaded polys. (yes, seriously, you did :) This time you'll learn a more fancy method for shading, namely gouraud shading. Gouraud shading has probably got it's name from some guy called A. A. Gouraud or something, I don't really know, but who cares anyways? :)

Gouraud shading is based on interpolating color values for a whole poly, based on 3 colors (one for each corner), to simulate light, for example. Polys drawn using gouraud shading will look similar to this:

Gouraud shading is done pretty much like flat-shading, but you also need to interpolate the color values vertically, and then draw the polys using a customized horizontal line drawing routine, which interpolates the line between two colors. The steps are shown in this picture:

First we'll make the line drawing routine, since we won't see if our gouraud poly routine really works unless we can draw the polys.

The line color values are calculated pretty much the same way we calculated the x values for the poly in the previous article, with the difference that we use the color and x distance for calculating the medium, instead of using the x and y values. The formula becomes cm = (c2 - c1) / (x2 - x1), where cm is the medium color distance, c1 is color 1, and you probably understand the rest.

Then the line drawing is done using good(?) old(!) PSET :)

Here's a code example:

'Made by Sane at the 22nd of May 2001, for QBCM' SUB gLINE (x1, x2, y, c1, c2) 'Sorting x and color values IF x1 > x2 THEN SWAP x1, x2: SWAP c1, c2 'Calculating of the medium distance between colors cm = 0 c = 0 IF c2 - c1 <> 0 AND x2 - x1 <> 0 THEN cm = (c2 - c1) / (x2 - x1) 'Drawing FOR x = x1 TO x2 PSET (x, y), c + c1 c = c + cm NEXT x END SUB

And now to the poly routine. We'll base the poly routine on the old, flat-shaded one, with the changes needed for gouraud shading.

The changes we'll make are:

'Made by Sane at the 22nd of May 2001, for QBCM SUB gpoly (xx1, yy1, xx2, yy2, xx3, yy3, cc1, cc2, cc3) 'Declare an array for storing slopes DIM poly(199, 1) 'Declare an array for storing color values DIM colr(199, 1) 'Point and color sorting IF yy1 < yy2 AND yy1 < yy3 THEN x1 = xx1: y1 = yy1: c1 = cc1 IF yy2 < yy1 AND yy2 < yy3 THEN x1 = xx2: y1 = yy2: c1 = cc2 IF yy3 < yy1 AND yy3 < yy2 THEN x1 = xx3: y1 = yy3: c1 = cc3 IF yy1 > yy2 AND yy1 > yy3 THEN x3 = xx1: y3 = yy1: c2 = cc1 IF yy2 > yy1 AND yy2 > yy3 THEN x3 = xx2: y3 = yy2: c2 = cc2 IF yy3 > yy1 AND yy3 > yy2 THEN x3 = xx3: y3 = yy3: c2 = cc3 IF yy1 <> y1 AND yy1 <> y3 THEN x2 = xx1: y2 = yy1: c3 = cc1 IF yy2 <> y1 AND yy2 <> y3 THEN x2 = xx2: y2 = yy2: c3 = cc2 IF yy3 <> y1 AND yy3 <> y3 THEN x2 = xx3: y2 = yy3: c3 = cc3 'Calculating of the slope and color blend from point 1 to point 2 m = 0 x = 0 c = 0 cm = 0 IF x1 + x2 <> 0 AND y1 + y2 <> 0 THEN m = (x1 - x2) / (y1 - y2) IF c1 + c2 <> 0 AND y1 + y2 <> 0 THEN cm = (c1 - c2) / (y1 - y2) FOR y = y1 TO y2 poly(y, 0) = x + x1 colr(y, 0) = c + c1 x = x + m c = c + cm NEXT y 'Calculating of the slope and color blend from point 2 to point 3 m = 0 x = 0 c = 0 cm = 0 IF x2 + x3 <> 0 AND y2 + y3 <> 0 THEN m = (x2 - x3) / (y2 - y3) IF c2 + c3 <> 0 AND y2 + y3 <> 0 THEN cm = (c2 - c3) / (y2 - y3) FOR y = y2 TO y3 poly(y, 0) = x + x2 colr(y, 0) = c + c2 x = x + m c = c + cm NEXT y 'Calculating of the slope and color blend from point 1 to point 3 m = 0 x = 0 c = 0 cm = 0 IF x1 + x3 <> 0 AND y1 + y3 <> 0 THEN m = (x1 - x3) / (y1 - y3) IF c1 + c3 <> 0 AND y1 + y3 <> 0 THEN cm = (c1 - c3) / (y1 - y3) FOR y = y1 TO y3 poly(y, 1) = x + x1 colr(y, 1) = c + c1 x = x + m c = c + cm NEXT y 'The easiest part, drawing FOR y = y1 TO y3 gLINE poly(y, 0), poly(y, 1), y, colr(y, 0), colr(y, 1) NEXT y END SUB

And some code for testing it:

'Made by Sane at the 22nd of May 2001, for QBCM SCREEN 13 'Changing the palette to greyscale FOR i = 0 TO 255 OUT &H3C8, i OUT &H3C9, i \ 4 OUT &H3C9, i \ 4 OUT &H3C9, i \ 4 NEXT i 'Setting color for PPS (Poly Per Second) rate text COLOR 255 oldtimer! = TIMER DO UNTIL INKEY$ = CHR$(27) x1 = INT(RND * 320) x2 = INT(RND * 320) x3 = INT(RND * 320) y1 = INT(RND * 200) y2 = INT(RND * 200) y3 = INT(RND * 200) gpoly x1, y1, x2, y2, x3, y3, INT(RND * 255), INT(RND * 255), INT(RND * 255) polynum = polynum + 1 IF TIMER > oldtimer! + 1 THEN LOCATE 1, 1: PRINT polynum: oldtimer! = TIMER: polynum = 0 LOOP

And just as last time, there's a file with the source available for you lazy guys who are too lazy to type it into QB yourselves :) This time it's named GPOLY.BAS

And a screenshot:

Note that there's a bug in it, which makes some of the polys have small gaps, I haven't managed to figure out why yet, as I wrote the code about 5 minutes ago, and I really need to get QBCM released this afternoon :) I might give you a better version in the next article, in case I've gotten rid of the bug by then...

This is mighty slow, as you might have noticed... Even though I've now got a Pentium 2 300 mHz (managed to fix it since last issue, when this comp was broken), I only get about 18 PPS(Polys Per Second), which really sucks...

Thus, the next parts subject will be:

**OPTIMIZING!***loads of applauses*

Next issue we'll speed up our poly drawing routines **a lot**, so that they even will be useful :)

I still haven't got one single comment about this series of articles, except for 3 people, who I know anyways. I'd love any feedback, please give me some :) And if you don't understand some/anything of this, I'm a polite guy, I won't make fun of you, just feel free to ask, come with suggestions for stuff to write about, suggestions for how to make this series better, criticism, you name it. I won't stop writing this series in case I don't get any feedback, as QBCM needs all content it can get, but I'd really appreciate feedback. I probably seem quite desperate about this, but I like feedback :) My email address, in case you've forgotten, is sane@telia.com

See ya in next issue,

-Sane