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.
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
presiona aqui para bajar programa
ejemplo utilizando el código descripto en este artículo