---------------------------------------------------------------------------
                            PHONG ILLUMINATION MODEL
              ---------------------------------------------------------------------------
                       an introduction by: FAC / Delabu Alama
              ---------------------------------------------------------------------------

 So, you just made your super-cool ultra-fast gouraud polygon filler that
 draws about 275451973 triangles per second and you're just like:

 "Yo, Gouraud looks real cool, but hey, this isn't exactly what I expected.
  My f*ck*ng torus looks like plastic! Where's the highlights? WTF????????"

 Well, if that's the case, then you're probably setting up your palette
 with a linear gradient, like this boring gray palette:

        for (i = 0; i < 64; i++) palette[i] = RGB(i, i, i);

 and unless you *really* want your objects to look like fake plastic,
 you got to get rid of that line up there and start using the phong model.

 Please note here that I AM NOT talking about Phong shading. I'll just
 tell you how to set up your palette to make Gouraud look more like Phong,
 (although you can do *real* phong shading using the linear palette).

 And you will also see that the Phong illumination model can be used to
 set up palettes for other effects, like particles or fire.
 

                             The Phong Equation

 I won't explain this using vectors (i.e.- the right way) because it's
 no use unless you're doing raytracing or complex graphics rendering,
 ...and because this way, I don't need to make no ASCII vector drawings.
 

 When light hits one face of a 3D object, the angle between the light
 vector and the face's normal is in the range from 0 to 90 degrees.
 And when you do something like the fake plastic grey palette up there, you
 get a linear relation between angle and illumination that looks like this:

                   illumination
                        |*
                        |  *
                        |    *
                        |      *
                        |        *
                        |          *
                        |            *
                        |              *
                        ---------------------  angle

 And that's why you got no highlight in your torus. A highlight is made
 when a small region of your object gets a big deal of light, making it
 very shiny. Outside that region, the light decays very quickly. That
 region is the part of your object that have a small angle (between the
 light vector and the object's normals). So, in the real life, if you
 remember, the illumination/angle graphic looks a little like this:
 

                   illumination
                        |**
                        |   **
                        |      *
                        |        *
                        |         *
                        |          *
                        |           *
                        |            *
                        ---------------------  angle

 Which is supossed to be exactly the graph of a cosine function for angles
 between 0 and 90 degrees. Now for shiny objects, the light intensity
 decays faster, so the above graphic looks kinda narrower. You better
 try to imagine it cuz I won't make any other stupid ASCII chart  :)

 Now, there's three types of components in the phong model:

        - Ambient light: This light is supposed to be everywhere, so
                         it's just added to the other components.
 

        - Diffuse light: This is light reflected by the object, so this
                         is actually what makes the object look green or
                         purple or whatever color you choose. This term
                         comes from a dot product between the light vector
                         and the object normal, so as you might have guessed,
                         this term is nothing but the graphic above, and
                         we'll calculate it using the cosine function.
 

        - Specular light: This is the term that generates the highlight.
                          It has a cosine form too, but to some power,
                          so its graphic can be made wider or narrower.
 

 We're almost done. Now all we need is to put all this stuff together.
 We will use three coefficients, for ambient, diffuse and specular light,
 so we obtain a formula like this:

       Illumination = Ka + Kd * cos(angle) + Ks * (cos(angle)) ^ N

 where:  Illumination is the amount of light reflected at some angle

         Ka is the ambient light coefficient

         Kd is the diffuse reflection coefficient

         Ks is the specular reflection coef... blah

         angle is the angle between (guess what) the normal and the light

         N is the specular reflection parameter, which controls how big
           and shiny the specular highlight is (or how narrow the specular
           graphic is).
 

 Ka, Kd, Ks and N are constant for a given object. Changing these will
 make the object look shinier, darker, metal-looking, plastic-looking
 or whatever you want. You can play with these values until you think
 your objects look cool enough.
 

 Now, all you have to do is make a loop, give angle values from 90 to 0
 degrees (because the palette is usually set up from darker to brighter),
 calculate the illumination for each angle and use it to set up the
 palette RGB values. Here's an example in C-udo code:

                angle = 90;
                for (i = 0; i < 90; i++) {
                        ambient = Ka;
                        diffuse = Kd * cos(angle);
                        specular = Ks * pow(cos(angle), N);
                        Illumination = ambient + diffuse + specular;
                        palette[i].red = Illumination;
                        palette[i].green = Illumination;
                        palette[i].blue = Illumination;
                        angle--;
                }

 Of course, this doesn't work quite well yet. First, we don't want only
 gray objects, so we need to use different coefficients for each RGB
 component, that makes 9 coefficients. The N parameter is the same for
 red, green and blue. And of course, real languages use radians, not
 degrees, so the range will be from  Pi/2 to 0.

 Another thing is that the last example uses palette colors from 0 to 89
 and people won't want to stick to that range, so it would be cool to
 use any palette range we want.

 Here's a better version of the function. It's not any difficult to figure
 it out, but I wrote it here for all of you copypasters. You know the rules,
 you use it, you credit me...
 

        void MakePhongPal( double Ra, double Rd, double Rs,
                           double Ga, double Gd, double Gs,
                           double Ba, double Bd, double Bs,
                           unsigned int N,
                           TPalette pal, int start, int range) {

            double diffuse, specular;
            int red, green, blue;
            double angle = 3.14159265 / 2.0;
            double angle_step = (3.14159265 / 2.0) / (double)range;

            for (int i = 0; i < range; i++) {

                diffuse = Rd * cos(angle);
                specular = Rs * pow(cos(angle), N);
                red = Ra + diffuse + specular;

                diffuse = Gd * cos(angle);
                specular = Gs * pow(cos(angle), N);
                green = Ga + diffuse + specular;

                diffuse = Bd * cos(angle);
                specular = Bs * pow(cos(angle), N);
                blue = Ba + diffuse + specular;

                if (red > 63) red = 63;
                if (green > 63) green = 63;
                if (blue > 63) blue = 63;

                pal[start + i].red = red;
                pal[start + i].green = green;
                pal[start + i].blue = blue;

                angle -= angle_step;
            }
        }
 

 Notes:  - Ra, Rd, Rs, Ga, Gd, Gs, Ba, Bd, Bs must be <= 63
         - the bigger N is, the smaller and brighter the highlight be
         - start is the first palette index to be used by the phong palette
         - range is the number of colors used
         - pal is the destination palette... change this to fit your stuff
 
 

 What else?

 Well, now your objects will look much better, but the phong model can be
 used for other cool stuff. I have used it to generate palettes for fire
 effects and they look more real than the usual lineal gradient.

 Particle systems look nicer too with this model, specially when the
 highlights are a different color than the diffuse light.

 If you optimize the above routine a little (like precalculating cosines
 and stuff) you can make some real nice palette morphings by just changing
 the coefficients. Try changing an object from plastic to metal look.

 You can also use the phong model in hicolor modes, like 16 bit or
 24 bit. Just make an array like this:

        // for 16 bit hicolor,  5/6/5 model

        unsigned short colorarray[256];

        void MakePhongPal ( same parameters as above ) {
                angle = Pi / 2.0;
                angle_step = Pi / 2.0 / 256.0;
                for (i = 0; i < 256; i++) {
                    (calculate the red, green and blue components)
                    if (red > 31) red = 31;
                    if (green > 63) green = 63;
                    if (blue > 31) blue = 31;
                    colorarray[i] = (red << 11) | (green << 5) | blue;
                }
        }
 

 and make you gouraud filler just the same, that is, interpolating one
 "color" value, then use that value as an index to the colorarray which
 contains the 16 bit phong color. You'll get a nice 256-color shaded
 gouraud object. Well, I haven't tried this myself, but I think it works
 and it's fairly fast. If you make it work, I'll be glad to see it.
 

 The example program (PHONG.ZIP)

 I included with this doc an example program that I made about more than a year
 ago to show how the phong model works. It was made in Turbo Pascal but it
 uses basically the same function I gave you. You can use it to play with
 the coefficient values and the N parameter. You just use the up/down
 arrows to place the white bar in the parameter you want to change and
 the left/right arrow to change it's value. Esc exits at any moment. In the
 upper part of the screen you can see how the palette changes when you vary
 the parameters.

 Note that the ambient light adds light to all the scene, not just the
 object. This can be used to do cool gradient palettes from one color
 to another (i.e.- use blue ambient with red diffuse + specular), which
 can be used for other effects.
 

 Final words

 Well, I hope this helps you make nicer palettes. If you use the code
 presented here or you think this document was useful, I'd love to be
 greeted in your programs, or by e-mail at least.

 I'd also like to hear your opinions/improvements/corrections about this
 document.

 You can email me at:   fac@slp1.telmex.net.mx
                                   shadowfac@hotmail.com

 and you can find me at #coders at UNDERnet and sometimes at IRCnet.

 And for all spanish speakers who are learning the basics, you can get
 my tutorials from the Hornet archive, in /code/tutorial/graphics or
 something like that.

 Thank youse

            http://members.xoom.com/delabualama/tutorial.html

            http://www.hornet.org/code/tutors/graphics

            http://galia.fc.uaslp.mx/~ganzo/prog/tutorial.html
 

           FAC / Delabu Alama