MODELO DE ILUMINACION DE PHONG
              ---------------------------------------------------------------------------
                            una introducción por: FAC / Delabu Alama
              ---------------------------------------------------------------------------

 A muchos coders (incluyendome a mi) nos ha pasado algo como lo siguiente:
 acabamos de programar una super rutina que dibuja alrededor de 8 millones
 de polígonos con sombreado gouraud en alta resolución (bueno, la m¡a no
 era tan rápida :). Y para probar la rutina usamos nuestro objeto 3D
 favorito (torus, duck, chrmface). Y es entonces cuando vemos que el
 sombreado gouraud no era todo lo que esper bamos: el objeto se ve opaco,
 no hay highlights, y si el objeto no tiene suficientes caras, se producen
 esas horribles bandas de tonos que hacen ver que gouraud no es otra cosa
 mas que una interpolación LINEAL.

 Si es ese el caso, entonces lo mas probable es que se est‚ usando
 una paleta con un gradiente lineal, por ejemplo, la t¡pica paleta
 gris que todos hemos usado:

        for (i = 0; i < 64; i++) {
                palette[i].red = i;
                palette[i].green = i;
                palette[i].blue = i;
        }
 

 Ese tipo de paletas est n bien para hacer pruebas, pero en un demo
 o en un juego hay que usar paletas mas interesantes. Y una forma de
 generarlas es usando el modelo de iluminación de Phong.

 Quiero que quede claro que NO estoy hablando de sombreado Phong. Ese
 tipo de sombreado es todavía muy lento. Solamente voy a indicarles
 cómo arreglar sus paletas para que el sombreado gouraud se vea mas
 como sombreado Phong (en objetos con suficientes caras).

 También veremos que el modelo de iluminación de Phong puede ser usado
 para generar paletas para otros efectos, como part¡culas o fuego.
 

                            La Ecuación de Phong

 La forma correcta de explicar esto es con vectores, sin embargo,
 en este documento no pienso usar vectores debido a que no es
 necesario a menos de que se piense programar un trazador de rayos
 o algo con sombreado phong *real*. La otra razón es que no tengo
 que hacer dibujos de vectores en ASCII.

 Bueno, cuando la luz llega a una cara de un objeto 3D, el  ngulo entre
 el vector de iluminación y la normal de los vértices est  entre 0 y 90
 grados (suponiendo que la luz está del lado visible de la cara). Si usamos
 una paleta con un gradiente linear, como la escala de grises que vimos
 hace un momento, obtenemos una relación lineal entre el  ngulo y la
 cantidad de iluminación. Una gr fica se ver¡a mas o menos así:

                   iluminación
                        |*
                        |  *
                        |    *
                        |      *
                        |        *
                        |          *
                        |            *
                        |              *
                        ---------------------  ángulo

 Y es por eso que no obtenemos un reflejo especular (highlight) en nuestro
 toro. Un "highlight" se produce cuando una región relativamente peque¤a
 del objeto refleja una gran cantidad de luz. Fuera de esa región, la
 iluminación decae r pidamente. Esa región es en donde la luz llega
 directamente, es decir, que el  ngulo entre el vector de iluminación
 y la normal de los vértices es muy pequeño. Por lo tanto, en la vida
 real, la gráfica  ángulo/iluminación debería verse así:

                   iluminación
                        |**
                        |   **
                        |      *
                        |        *
                        |         *
                        |          *
                        |           *
                        |            *
                        ---------------------   ángulo

 Esta gr fica es muy parecida a la gráfica del coseno entre 0 y 90 grados.

 Ahora, en algunos objetos (por ejemplo, objetos metálicos), la intensidad
 de la luz decae mas rápidamente, por lo que la gr fica anterior debería
 verse mas estrecha (pero siempre de 0 a 90 grados):

                   iluminación
                        |**
                        |  **
                        |    *
                        |     *
                        |      *
                        |       *
                        |        **
                        |          ***
                        ---------------------   ángulo

 (traten de imaginarse la gráfica porque mi ascii es una m**rda)
 

 Bueno, la iluminación en el modelo Phong se forma a partir de tres
 componentes, las cuales al sumarse nos producen gráficas como las
 anteriores. Esas componentes son:

        - Luz Ambiental: Esta luz se produce por reflexiones en todas
                                  direcciones, por lo tanto está  en todos lados.
                                  Esta componente solo se suma a las demás.
 

        - Luz Difusa: Esta es la luz reflejada por el objeto, as¡ que
                            esta componente es la que hace que el objeto se
                            vea verde, azul o de cualquier otro color.

                          El término difuso se obtiene a partir del producto
                          punto entre el vector de iluminación y las normales
                         del objeto, por lo tanto, ‚ste es el t‚rmino que
                         produce la seguna gr fica que vimos, y por si no
                         lo han adivinado: usaremos la función coseno para
                         calcularlo.
 

        - Luz Especular: Este es el término que genera el "highlight".
                                  También se calcula usando el coseno, pero
                                 elevado a alguna potencia. Eso es lo que hace
                                 la gráfica mas estrecha.
 

 Ahora solamente tenemos que juntar todos los t‚rminos. Vamos a usar
 tres coeficientes: para iluminación ambiental, difusa y especular,
 y al final obtenemos una fórmula como ésta:

       Iluminación = Ka + Kd * cos(ang) + Ks * pow(cos(ang), N)

 donde:  Iluminación es la cantidad de luz reflejada para alg£n  ngulo

         Ka es el coeficiente de iluminación ambiental

         Kd es el coeficiente de reflexión difusa

         Ks es el coeficiente de reflexión especular

         ang es el  ngulo entre (que creen?) la normal y el vector de la luz

           N es el par metro de reflexión especular, que controla el tamaño
           y la intensidad del reflejo especular (en otras palabras, nos
          dice qu‚ tan estrecha es la gráfica  ángulo/iluminación).
 

 Ka, Kd, Ks y N son constantes para algún objeto dado. Jugando con
 estos valores podemos hacer que el objeto cambie de color, que se
 vea mas brillante, mas opaco, mas oscuro, met lico, plástico, etc...

 Ahora, todo lo que hay que hacer es un ciclo para llenar la paleta.
 Dentro del ciclo variamos el  ngulo desde 90 hasta 0 (debido a que
 usualmente los primeros colores de la paleta son los mas oscuros),
 calculamos la iluminación para cada  ngulo y usamos ese valor para
 obtener los valores RGB de la paleta. Por ejemplo, en C-udo código:

                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--;
                }

 Por supuesto que la rutina anterior no funciona muy bien. Primero,
 no queremos objetos grises  únicamente, así que tenemos que usar
 coeficientes distintos para cada componente RGB, lo cual nos da
 un total de 9 coeficientes. El par metro N es el mismo para las
 tres componentes RGB. Y por supuesto, tenemos que convertir los
 grados a radianes, o dicho de otra forma, hacemos variar el  ángulo
 desde Pi / 2  hasta cero.

 También debemos poder usar cualquier rango de colores de la paleta.
 El ejemplo anterior solamente usaba los colores del 0 al 89, lo cual
 no es muy com£n. Incluso podr¡amos tener varios degradados de Phong
 en la misma paleta.

 Aquí hay una mejor versión de la función anterior. No es muy difícil
 de entender, pero la escrib¡ para que la puedan copypastear directamente
 en sus programas. Ya conocen las reglas: si la usan, denme crédito...

        typedef unsigned char TPalette[256][3];

        void PaletaPhong( double Ra, double Rd, double Rs,
                          double Ga, double Gd, double Gs,
                          double Ba, double Bd, double Bs,
                          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].[0] = red;
                pal[start + i].[1] = green;
                pal[start + i].[2] = blue;

                angle -= angle_step;
            }
        }
 

 Notas:  - Ra, Rd, Rs, Ga, Gd, Gs, Ba, Bd, Bs  deben estar entre 0 y 63
         - entre mas grande es N, el "highlight" ser  mas pequeño y brillante
         - start es el primer índice de la paleta que ser  usado
         - range es el n£mero de colores que se usar n en el degradado
         - pal es la paleta destino
 
 

 Qué sigue?

 Bueno, ahora los objetos 3D se ver n mucho mejor, pero las paletas
 generadas con el modelo Phong se pueden usar en otras cosas. Yo las
 he usado en sistemas de partículas, efectos de fuego y motion blur,
 transparencia e incluso para mapeo de textura con sombreado. Un
 efecto interesante es hacer que la iluminación difusa sea de un
 color completamente distinto que la iluminación especular.

 Optimizando un poco la rutina anterior (precalculando cosenos, etc),
 se pueden hacer morphings de paleta con solo variar algunos coeficientes
 y recalculando la paleta en cada frame. De esta forma se pueden obtener
 cambios de colores, flashazos y otros efectos de iluminación.

 Tambi‚n se puede usar el modelo phong en modos hicolor o truecolor,
 por ejemplo, la rutina para modos de 16 bits podría ser así:

        //  16 bit hicolor,  5/6/5

        unsigned short colorarray[256];

        void PaletaPhong( mismos par metros que la otra funci¢n ) {
                angle = Pi / 2.0;
                angle_step = Pi / 2.0 / 256.0;
                for (i = 0; i < 256; i++) {
                    /* calcula aqu¡ las componentes RGB */
                    if (red > 31) red = 31;
                    if (green > 63) green = 63;
                    if (blue > 31) blue = 31;
                    colorarray[i] = (red << 11) | (green << 5) | blue;
                }
        }

 y luego hay que hacer la rutina que dibuja los pol¡gonos de la misma
 forma que si se usara una paleta de 256 colores, es decir, interpolando
 un valor entre 0 y 255. Solamente que ese valor lo usamos para buscar
 en la tabla colorarray y de ah¡ obtenemos el color de 16 bits. De hecho,
 as¡ es exactamente como funciona en realidad la paleta. De esta forma
 se pueden obtener objetos sombreados con 256 tonos, y además, el
 método es relativamente rápido.
 

 El programa de ejemplo

 Junto con este documento incluyo un programa que hice hace algun tiempo
 en Turbo Pascal para mostrar el modelo de iluminación Phong. (phong.zip)
 Para usarlo simplemente hay que seleccionar el valor a modificar con las
 flechas arriba/abajo, y con las flechas izquierda/derecha podr n modificar
 el valor seleccionado. Para salir del programa opriman ESC.

 En la parte superior de la pantalla se puede ver el degradado que
 se genera al modificar los par metros.

 Noten que la luz ambiental ilumina toda la escena, no solamente el
 objeto. Esto se puede usar para hacer degradados de un color a otro.
 

 Final

 Bueno, espero que esto les ayude a crear mejores paletas. Si el código
 presentado aquí, o el documento les ha sido útil, me gustar¡a saberlo
 y/o obtener algún reconocimiento dentro de sus programas.

 También me gustar¡a conocer sus opiniones, correcciones y mejoras
 acerca de este documento. Pueden hacerlo por e-mail a:

                       fac@slp1.telmex.net.mx
                       shadowfac@hotmail.com

 y también pueden encontrarme en IRC, en el canale #coders de Undernet.

 Y finalmente, para todos aquellos que estan aprendiendo las bases
 de la programación de demos y gráficos, pueden obtener mis tutoriales
 de los siguientes lugares:

            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
 

   presiona aqui para bajar programa ejemplo utilizando el código descripto en este artículo