QB CULT MAGAZINE
Vol. 2 Iss. 2 - May 2001

Graphics Programming, Part 2
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:

  • Adding an array for storing the y color values for the poly, similar to the one for slopes
  • Sorting colors at the same time we sort the points, to make the colors stay at the points they're supposed for
  • Calculating the color blend between points, while also calculating the slopes
  • Altering the drawing part slightly
    '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