Buenas, continúo con la saga de tutoriales para programar 3D para NDS. En esta ocasión vamos a aprender a manejar una forma más rápida de dibujar, las Listas Compiladas,
Aprenderemos sobre iluminación, materiales y luces. Además, aprenderemos qué son las normales de iluminación y cómo se usan.
Bien, tras dar la explicación, empezamos!
1- Listas compiladas, renderizado veloz
Últimamente hemos estado dibujando 3D con las funciones glBegin(), y después definíamos los vértices y colores que va a tener nuestro objeto, esta era una forma de dibujar 3D, solo que algo lenta y más todavía si empleamos cálculos matemáticos para calcular los vértices. Pues shoy voy a presentaros la solución a este problema , las listas compiladas.
Las listas compiladas son, básicamente, un array de datos en el que están todos los vértices y colores de nuestro objeto a dibujar. Es muy sencillo, primero creamos el array, y luego lo dibujamos con la función glCallList():
void glCallList(u32 *array_lista); Dibuja una lista compilada en la pantalla array_lista --> Buffer de datos de vértices que se van a dibujar Nota: Los datos de la lista deben estar en U32 (unsigned int)
Pero ahora tenéis que aprender lo más importante, es decir, cómo hacer ese array, bueno, primero os pongo un ejemplo y después lo explico:
u32 triangulo[] = { 12, FIFO_COMMAND_PACK(FIFO_BEGIN, FIFO_COLOR, FIFO_VERTEX16, FIFO_COLOR), GL_TRIANGLE, RGB15(31,0,0), VERTEX_PACK(floattov16(-1),floattov16(-1)), VERTEX_PACK(0,0), RGB15(0,31,0), FIFO_COMMAND_PACK(FIFO_VERTEX16, FIFO_COLOR, FIFO_VERTEX16, FIFO_END), VERTEX_PACK(floattov16(1),floattov16(-1)), VERTEX_PACK(0,0), RGB15(0,0,31), VERTEX_PACK(floattov16(0),floattov16(1)), VERTEX_PACK(0,0), };
Os habréis quedado de priedra, ¿no? Pues esto es para dibujar un simple triángulo, pero no os preocupéis, ahora os explicaré cada comando para poder crear nuestra lista compilada.
Lo primero que hay que saber es que el primer número debe ser siempre, repito, siempre, el número de comandos que hay en la lista (el numero de variables vamos). En el ejemplo anterior, si contáis desde el segundo valor del array, os sale 12, ese es el tamaño de la lista.
Después tenemos que decir lo que vamos a hacer, para ello usamos esta macro:
FIFO_COMMAND_PACK(comando1, comando2, comando3, comando4) Define 4 cosas que quieres hacer en los argumentos comando1, comando2, comando3 y comando4 Dichos argumentos pueden ser los siguientes - FIFO_BEGIN --> Definir el modo de dibujar (como glBegin()) - FIFO_COLOR --> Definir el color que usaremos (como glColor3b())(va de 0 a 31) - FIFO_VERTEX16 --> Define un vértice en V16 (como glVertex3f()) - FIFO_NOP --> Si nos sobran acciones ponemos esto y no se hará nada - FIFO_END --> Termina de dibujar (indica el final de la lista compilada) Ejemplo: FIFO_COMMAND_PACK(FIFO_BEGIN, FIFO_COLOR, FIFO_VERTEX16, FIFO_END) Empaqueta 4 comandos, los cuales indican el modo de dibujar, el color de vertice, el vertice, y el fin de la lista
Ahora que hemos definido las cosas que queremos hacer con FIFO_COMMAND_PACK(), tenemos que hacer lo que le hemos dicho.
Si hemos definido FIFO_BEGIN, ponemos GL_TRIANGLES o GL_QUADS, ya que es el mismo argumento que la función glBegin()
Si hemos definido FIFO_COLOR, debemos poner el color que usaremos (como en glColor3b()), usando la macro RGB15(), ejemplo:
RGB15(31, 0, 0); // Rojo
Si hemos definido FIFO_VERTEX16, esto significa que debemos usar 2 espacios del array para el vértice, 1 para X e Y, y otro para Z, ejemplo:
VERTEX_PACK(x, y), // Coordenadas XY VERTEX_PACK(z, z), // Coordenada Z (se pone 2 veces)
Si hemos puesto FIFO_NOP o FIFO_END, no hace falta poner nada en la lista.
Para terminar este embrollo, os pongo el ejemplo de antes pero ahora comentado:
u32 triangulo[] = { // Empezamos el array (cada miembro es un int) 12, // Numero de comandos que usaremos (en este caso 12) FIFO_COMMAND_PACK(FIFO_BEGIN, FIFO_COLOR, FIFO_VERTEX16, FIFO_COLOR), // Definimos lo que vamos a hacer (modo de dibujo, color, vertice, color) GL_TRIANGLE, // Como pusimos FIFO_BEGIN, decimos que usaremos triangulos RGB15(31,0,0), // El segundo es FIFO_COLOR, pues ponemos el color con la macro RGB15() VERTEX_PACK(floattov16(-1),floattov16(-1)), // El tercero es un vértice, definimos la XY VERTEX_PACK(0,0), // y en el siguiente la Z (recordar que las coordenadas estan en V16) RGB15(0,31,0), // El cuarto comando es un color, ponemos el color FIFO_COMMAND_PACK(FIFO_VERTEX16, FIFO_COLOR, FIFO_VERTEX16, FIFO_END), // Definimos otros 4 comandos VERTEX_PACK(floattov16(1),floattov16(-1)), // Vertice XY VERTEX_PACK(0,0), // luego Z RGB15(0,0,31), // Color VERTEX_PACK(floattov16(0),floattov16(1)), // Vertice XY VERTEX_PACK(0,0), // luego Z // Como el ultimo es FIFO_END, no se pone nada }; glCallList(triangulo); // Renderiza la lista compilada (el triangulo)
Si tenéis alguna duda sobre las listas compiladas, ponedla en los comentarios.
Bien, ahora seguro que os preguntaréis: ¿Todo esto para u triángulo?¿No hay una forma más fácil para dibujar rápido?
Pues no, no hay, pero tampoco tenemos que hacer los comandos nosotros necesariamente, en Internet rondan muchos convertidores de modelos 3D a listas compiladas NDS, (nosotros usaremos uno de ellos), por lo que con solo cargarlo en RAM y pasárselo al glCallList() tendremos nuestra lista.
2- Usando materiales
Bien, hemos estado usando hasta ahora sólo colores planos para nuestros objetos 3D, ¡cuando podemos ponerle más cosas de colores al objeto! Esto se logra gracias a los materiales, pero cuando usamos materiales, debemos usar también luces, si no, los valores del material no serán visibles, dichos valores del material son los siguientes:
Difusa: Es el color que se muestra cuando entra en contacto con la luz, se rebota en todas direcciones (el color) Ambiental: Es el color de una zona no iluminada (no entra en contacto con la luz Especular: Es el color que se refleja a la cámara cuando le da la luz Emisión: Es la cantidad de luz que emite el propio objeto, sin necesidad de luces
Bien, esos son los atributos que se le dan a los materiales, ¿recordamos la macro RGB15() de las listas compiladas? Bien, pues esa macro la usaremos mucho para dar los valores a los materiales (que deben estar en formato NDS). Para definir un atributo de un material usamos esta función:
glMaterialf(GLenum atributo, u16 color); Define un atributo para el material usado atributo --> El atributo a definir, puede ser: GL_DIFFUSE -> Difusa GL_AMBIENT -> Ambiental GL_SPECULAR -> Especular GL_EMISSION -> Emisión color --> En color en RGB15() para el atributo de antes
Un ejemplo de cómo se usa un material:
// Define material glMaterialf(GL_DIFFUSE, RGB15(31, 0, 0)); // Difusa rojo glMaterialf(GL_AMBIENT, RGB15(15, 15, 15)); // Ambiental gris glMaterialf(GL_SPECULAR, RGB15(31, 31, 31)); // Especular blanca glMaterialf(GL_EMISSION, RGB15(0, 0, 0)); // Emision negra (no emite luz) // Dibujar objeto
Como véis en el código, vamos definiendo cada uno de los atributos del material con glMaterialf(), y después dibujamos el objeto que le va a afectar.
3- Luces, cámara, acción
Hemos visto que para que los materiales se vean es necesario el uso de las luces, en NDS estamos limitados a 4 de ellas y éstas sólo pueden tener atributos de color y de posición (el color de la luz y su posición en el espacio XYZ).
Para usar una luz, lo primero que hay que hacer es decirle al encargado de atributos de polígonos que vamos a usar luces (glPolyFmt()), para ello hacemos lo siguiente:
glPolyFmt(POLY_ALPHA(31) | POLY_CULL_NONE | POLY_FORMAT_LIGHT0); /* * Atributos normales y activa la luz 0 con POLY_FORMAT_LIGHT0 * Para las luces 1, 2, y 3 es lo mismo, por ejemplo * la luz 2 sería POLY_FORMAT_LIGHT2 */
Tras haber definido la luz, tenemos que configurarla, es decir, definir su color(difusa) y su posición en el espacio. Para ello usamos esta función:
void glLight(int numero_de_luz, u16 color, v10 x, v10 y, v10 z); Configura la luz "numero_de_luz" con un color "color" y colócalo en "x" "y" "z" numero_de_luz --> Numero de luz a configurar (0, 1, 2 o 3) color --> El color a usar en RGB15() x, y, z --> Las coordenadas de posición de la luz
Un ejemplo de cómo usar una luz con un material
// INICIAMOS LA LUZ 0 glPolyFmt(POLY_ALPHA(31) | POLY_CULL_NONE | POLY_FORMAT_LIGHT0); // CONFIGURAMOS LUZ 0, DE COLOR BLANCO Y EN LA POSICION 2, 2, -2 glLight(0, RGB15(31, 31, 31), floattov10(2), floattov10(2), floattov10(-2)); // Las coordenadas deben ser en V10, con una macro la convertimos // CONFIGURAMOS EL MATERIAL glMaterialf(GL_DIFFUSE, RGB15(31, 0, 0)); // Difusa rojo glMaterialf(GL_AMBIENT, RGB15(15, 15, 15)); // Ambiental gris glMaterialf(GL_SPECULAR, RGB15(31, 31, 31)); // Especular blanca glMaterialf(GL_EMISSION, RGB15(0, 0, 0)); // Emision negra (no emite luz) // DIBUJAMOS EL OBJETO // falta codigo
Bien, así es cómo se configura una luz (poniéndolo en atributos de polígonos, diciendo su color y posición) para un material afectado. El último paso para que el objeto se vea es aplicarle las normales de iluminación, para que los materiales y luces surtan efecto.
4- Las normales de iluminación
Llegamos al último apartado, en el cual vamos a ver qué son las normales de iluminación y cómo se usa, algo que es obligatorio, para que al objeto le afecte la luz y los materiales definidos anteriormente, en caso contrario, el objeto no se verá.
Bueno, las normales son, por así decirlo, un vector (o línea) cuyo inicio es la luz y el destino es el objeto afectado por ella, en el cual se refleja la cantidad de luz a la que afecta cada uno de los polígonos de un objeto 3D. Con esto no llegamos a ninguna parte así que os pondre una imagen para que lo entendáis:
En la imagen vemos usa esfera a la que se le ha aplicado una luz. La luz, al chocar con el objeto, rebota con el mismo ángulo de entrada (dependiendo de la orientación del polígono), y rebota lo que llamamos el color del objeto, pero, si os fijáis bien, unas partes son más luminosas y otras más oscuras, esto se debe a la cantidad de luz que ha recibido cada polígono (esto está en factor con la distancia). Bueno, pues eso es lo que hacen las normales, decir la cantidad de luminosidad de uno o varios vértices.
Ahora que sabemos lo que son, vamos a definirlas, para ello usamos la función glNormal3f(x, y, z):
void glNormal(float x, float y, float z); Define la normal de un vértice/s parámetros -> la posición del vector que afecta a la luz, según qué valor de démos la iluminación del polígono se verá afectada de una manera o de otra.
Bien, recordad que la normal siempre se define antes que el vértice afectado, además, varios vértices pueden compartir la misma normal, es decir, que tenga la misma iluminación.
Bueno, pues terminamos con el ejemplo final de cómo usar las luces, materiales y normales para dibujar un objeto luminoso:
// INICIAMOS LA LUZ 0 glPolyFmt(POLY_ALPHA(31) | POLY_CULL_NONE | POLY_FORMAT_LIGHT0); // CONFIGURAMOS LUZ 0, DE COLOR BLANCO Y EN LA POSICION 2, 2, -2 glLight(0, RGB15(31, 31, 31), floattov10(2), floattov10(2), floattov10(-2)); // Las coordenadas deben ser en V10, con una macro la convertimos // CONFIGURAMOS EL MATERIAL glMaterialf(GL_DIFFUSE, RGB15(31, 0, 0)); // Difusa rojo glMaterialf(GL_AMBIENT, RGB15(15, 15, 15)); // Ambiental gris glMaterialf(GL_SPECULAR, RGB15(31, 31, 31)); // Especular blanca glMaterialf(GL_EMISSION, RGB15(0, 0, 0)); // Emision negra (no emite luz) // DIBUJAMOS EL OBJETO glBegin(GL_TRIANGLES); // Triangulos glNormal3f(0, 0, 1); // Definimos la normal y colocamos el vector justo delante del objeto (Z = 1.0)(así el objeto será muy luminoso) glVertex3f(0, 1, 0); // Si usamos materiales, no hace falta definir colores, si lo hacemos las luces no valdrán para nada glVertex3f(0, 0, 0); glVertex3f(1, 0, 0);
Bueno, esto es todo lo que necesitáis saber sobre iluminaciones en NDS, os pongo ahora un par de ejercicios para asimilar lo que hemos aprendido en este tutorial.
Ejercicios
Ejercicio 4
Este ejercicio está abordado en las listas compiladas, gracias a un conversor de modelos 3D para NDS podemos mostrar figuras geométricas más complejas, dicho programa está junto a la descarga de este ejercicio.
Nota: Los modelos que vayamos a convertir deben estar en formato *.3DS, y sólo pueden tener un material y un objeto.
También dibujaremos un triángulo y un cuadrado con los comandos FIFO que hemos visto hoy.
Con Arriba y abajo seleccionas el objeto (texto, esfera o cono)
Con LR mueves X
Con AB rotas Y
Con XY escalas Z
Con + mueves la cámara
Descarga: http://www.mediafire.com/?xn3u61rulnessk6
Ejercicio 5
En este ejercicio usaremos todo lo que sabemos sobre iluminación (normales, luces y materiales), usaremos 6 materiales distintos para cada una de las caras del cubo, las 4 luces de la NDS y aplicaremos normales de iluminación a las caras del cubo. Puedes cambiar la luz seleccionada a ver qué pasa.
Con Arriba-Abajo cambias la luz seleccionada
Con el Stylus mueves la cámara
Descarga: http://www.mediafire.com/?pmpv85u4yhvhjsq
Bueno, este es el fin de este tercer tutorial, en el siguiente aprenderemos a usar las texturas y los modos de proyección.
Bien, hasta entonces, saludos!
~Actualmente estudiando Ingeniería de las Tecnologías de la Telecomunicación en la Escuela de Ingenieros~
Continuas con
Continuas con la serie de tutoriales, estan todos muy pero que muy bien.
Una pregunta: Si adapto el código esto me podría servir para otras plataformas, como por ejemplo la PSP?
Si
Si, los contenidos 3D son los mismos, pero las funciones cambian, depende de si usas C o Lua
Usaré C
Usaré C, así aprendo más de este lenguaje, además me sirve para casi cualquier plataforma...
Saludos!