Y se hizo la luz...

Si, ya se que es un titulo muy cutre :-) pero bueno, el tutorial lo hago yo y lo llamo como quiera, asi que AJO y AGUA , jejeje :-P

y como su nombre indica... este tutorial va sobre como añadir los focos de luz a nuestro mundo 3D. vamos a llamarlo "Lighting" por comodidad, ya que es el nombre que se le da en todos lados y es dificil de traducirlo al castellano sin que suene "cursi" :)

A la hora de añadir los efectos de luz a nuestro programa, existen 3 elementos que interviene en el resultado final, estos son:

- color del material del objeto (y la textura)

- el color difuminado y especular de un vertice

- el color y la intensidad de la luz producida en la escena.

veamos cada uno de estos elementos un poco mas en detalle:

MATERIALES

Por defecto un objeto no tiene material. Cuando no se selecciona ningunmaterial, Direct3D desactiva cualquier efecto de luz. Asi que especificar un material es algo bastante importante :)

D3DUtil_InitMaterial() establece los valores RGBA (Red, Gree, Blue, Alpha) para un material. Estos valores representan que cantidad de luz se refleja sobre la superficie que se renderiza con ese material. Entre las propiedades de un material se encuentran reflejo difuminado, reflejo de ambiente, emision de luz, destello especular, etc... Veamos cada uno: (los nombres los he mantenido en ingles, ya que tambien en el codigo fuente y en los ejemplos del SDK se usan estos nombres)

- Diffuse Reflection: Como el poligono refleja la luz difusa (esta es la que no proviene del ambiente). Se describe como que color se refleja mejor sobre el poligono. Es decir, otros colores excepto el especificado se reflejan en menor cantidad.

- Ambient Reflection: Define como el poligono refleja la luz ambiental. Se describe igual que el anterior, que color se refleja mejor sobre el poligono.

- Light Emission: Hace que parezca como si el poligono emitiese alguna luz (no afecta al resto del mundo, tan solo a la apariencia del poligono)

- Specular highlighting. Describe como de brillante es el poligono.

Ejemplo:

Un material cuyos componentes sean R=1, G=1, B=1, A=1 reflejara toda la luz que le llegue. En camio R=0, G=1, B=0, A=1 reflejara solo la luz verde que le llegue.

Para todas estas cosas utilizaremos la funcion SetMaterial()

D3DUtil_InitMaterial( mtrl, m_mpObjects[0].r, m_pObjects[0].g, m_pObjects[0].b );
m_pd3dDevice -> SetMaterial( &mtrl );

MODELOS DE LUZ

Tras configurar el material, debemos poner las luces. Los colores que se ponen como componente a una luz representan la cantidad de una determinada componente de la luz se emite. En las luces no se usa la componente Alpha, solo existen las componentes habituales RGB (Red, Gree, Blue)

Asi por ejemplo, R=1, G=1, B=1 crea una luz brillante. R=0, G=0, B=0 no emite luz del todo.

Tambien puedes jugar con los componentes para crear una luz red, verde, azul, amarillo, purpura, etc...

Para crear luces "oscuras" debes poner valores negativos, es decir, que en lugar de dar luz lo que hacen es quitarla.

Si especificas un valor mayor de 1.0 lo que haces es crear una luz extremadamente brillante.

Direct3D emplea 3 tipos de luces: PointLights, SpotLights, Directonial Lights. De todas ellas evita utilizar las SpotLights porque existe algun truco para generar el mismo efecto y queda mucho mejor (usanto mezclas de texturas, pero eso ya lo veremos mas adelante)

Vamos a crear una luz ambiental (la mas tipica) con el siguiente codigo:

m_pd3dDevice -> SetRenderState ( D3DRS_AMBIENT, 0x0c0c0c0c );

Esto crea una luz gris que afecta a todos los objetos de la escena, no existe intensidad... afecta a todos por igual aunque esten mas lejos o cerca ya que la luz de ambiente no tiene origen ... excepto si los objetos tienen diferentes materiales porque en tal caso el material condiciona la forma en que se refleja la luz (como he explicado antes)

Las "Directional Lights" siempre tienen una direccion a la que apuntan, y por supuesto el color. Antes de crear luces direccionales deberiamos comprobar que nuestra tarjeta admite este tipo de luz. Para crear la luz direccional usamos SetLight(), que utiliza la estructura D3DLIGHT8:

typedef struct _D3DLIGHT8 {
D3DLIGHTTYPE dltType;
D3DCOLORVALUE dcvDifuse;
D3DCOLORVALUE dcvSpecular;
D3DCOLORVALUE dcvAmbient;
D3DVECTOR dvPosition;
D3DVECTOR dvDirection;
D3DVALUE dvRange;
D3DVALUE dvFalloff;
D3DVALUE dvAttenuation0;
D3DVALUE dvAttenuation1;
D3DVALUE dvAttenuation2;
D3DVALUE dvTheta;
D3DVALUE dvPhi;
} D3DLIGHT8, *LPD3DLIGHT8;

La posicion, rango, y atenuacion se usan para definir la localizacion de la luz en el espacio-mundo, y como se comporta respecto a la distancia.

El sombreado consiste en calcular la luz de modo que determina el color de cada pixel. Por defecto se utiliza el sombreado llamado Gouraud. Este tipo de sombreado determna la luz de cada vertice de cada triangulo, y esta luz se interpola sobre la superficie del triangulo. Para controlar este sombreado puedes utilizar distintos vectores normales para los vertices. Pero esto lo veremos mas adelante, por ahora concentremonos en la luz... vamos a crer ya nuestra luz direccional:

D3DUtil_InitLight( light, D3DLIGHT_DIRECTIONAL, 0.5f, -1.0f, 0.3f );
m_pd3dDevice -> SetLight( 0, &light );
m_pd3dDevice -> LightEnable( 0, TRUE);
m_pd3dDevice -> SetRenderState( D3DRS_LIGHTING, TRUE );

Vertex Color

Hay algo que afecta todavia mas que el material de una superficie, y es el color de sus vertices. No siempre se usan, pero si quieres usarlos la manera mas facil es incluir este color en la definicion de la estructura de los vertices. (En el primer tutorial usabamos Vertex Color para hacer nuestro primer programa)

struct CUSTOMVERTEX
{
FLOAT x,y,z,rhw; //posicion del vertice
DWORD color; //color del vertice
};

//Descripcion de nuestra estructura de vertices
#define D3DFVF_CUSTOMVERTEX( D3DFVF_XYZRHW | D3DFVF_DIFFUSE )

Como ya hemos usado Vertex Color antes, para el proximo programa usaremos Materiales que son mas interesantes :)