Anuncios Google

Tutoriales de programación 3D para NDS: 5- Transparencias, niebla y 3D dual

Buenas, continúo con la saga de tutoriales para programar 3D para NDS. En esta ocasión veremos las transparencias (el poner un objeto transparente encima de otro con el fondo), la niebla (para simular sombreados) y aprenderemos a usar 3D en ambas pantallas (dije que no se podía, pero hay una forma de hacerlo mediante código)

Bueno, sin más preámbulos, empezamos!

 

1- Interactuando con transparencias en NDS

Bien, hasta ahora hemos estado dando casi todo lo que permite la NDS en 3D, ahora es el turno de los "efectos especiales" que admite (transparencias, sombreados y entornos), que son las cosas que vamos a dar en estos tutoriales que faltan.

Bueno, y ahora llega el turno de las transparencias, esto nos va a permitir que un objeto sea transparente, semitransparente, o que tenga el modo wireframe (solo aristas), además la NDS nos permite hacer un objeto transparente sobre otro, siempre que tengan distinta ID. Muchos conceptos sin explicar pero ahora los vamos a ver todos, primero veamos un pseudocódigo de cómo vamos a usar las transparencias en este tutorial:

int main(){
		Inicia3D();
 
		ACTIVAMOS TRANSPARENCIAS
 
		while(1){
			Empezar3D();
 
			APLICAR TRANSPARENCIA A UN OBJETO
			DIBUJAR OBJETO
 
			APLICAR TRANSPARENCIA A OTRO OBJETO
			DIBUJAR OTRO OBJETO
 
			Actualiza3D();
		}
	}

Bien, esto de las transparencias es más fácil de lo que parece, pero con saltarnos un solo detalle el objeto no sería transparente.

Lo primero que vemos es que tenemos que decirle a OpenGL que queremos usar transparencias, como solemos usar, con la función glEnable():

code
    glEnable(GL_BLEND); // Activa transparencias
	glDisable(GL_BLEND); // Desactiva transparencias
code<

También hay otro modo de hacer transparencias, es con GL_ALPHA_TEST, lo veremos después de esto...

Bien, una vez inicializado el motor de transparencias, pasamos al bucle de dibujado, una vez allí, tenemos que aplicar con glPolyFmt() los atributos de transparencias, hacemos esto:

glPolyFmt(POLY_ALPHA(alpha) | POLY_ID(id) | POLY_CULL_NONE);
	    alpha --> El valor de transparencia del objeto/s afectado/s, va de 0 a 31
		          31 = objeto sólido
				  1 - 30 = objeto transparente
				  0 = Solo se ven las aristas (modo wireframe)
		id ---> Tenemos que darle un ID al polígono/s afectado/s, la NDS
		        soporta 63 IDs o Slots para las transparencias, tenemos que
				poner un número distinto para cada glPolyFmt() para que el objeto
				pueda ser transparente encima de otro
		POLY_CULL_NONE --> Importante, no debemos activar el culling cuando hacemos
		                   transparencias, puesto que salen cosas muy raras al hacerlo,
						   lo desactivamos con POLY_CULL_NONE

Al tener puestos los atributos de transparencia, dibujamos el objeto transparente, por medio de glBegin() o por listas compiladas. Si queremos cambiar los valores de transparencia pues solo añadimos otro glPolyFmt() y listos.

Un ejemplo de cómo usar las transparencias:

// Activa Alpha-Blending
   glEnable(GL_BLEND);
 
   while(1){
		Empieza3D();
 
		// Atributos del objeto 1
		glPolyFmt(POLY_ALPHA(14) | POLY_ID(0) | POLY_CULL_NONE);  // Es semitransparente con ID 0
		glCallList(modelo1);  // Dibujamos el modelo
 
		// Objeto 2
		glPolyFmt(POLY_ALPHA(31) | POLY_ID(1) | POLY_CULL_NONE); // Es solido con ID 1
		glCallList(modelo2); // Lo dibujamos
 
		// Objeto 3
		glPolyFmt(POLY_ALPHA(0) | POLY_ID(2) | POLY_CULL_NONE); // Esta en modo wireframe (solo se ven aristas)
		glCallList(modelo3); // Dibujamos
 
		Actualiza3D();
   }

Bien, así es como se usan las transparencias del modo más básico, pero también podemos hacer un objeto totalmente transparente (que no se vea ni se procesen sus caras), para ello usamos el GL_ALPHA_TEST:

glEnable(GL_ALPHA_TEST | GL_BLEND); // OpenGL nos pondrá el Alpha a los objetos mediante un patrón
	glDisable(GL_ALPHA_TEST | GL_BLEND); // El Alpha lo tendremos que poner nosotros

Una vez iniciada la transparencia total, tenemos que darle un patrón para que ponga transparentes los que su transparencia sea menor que el patrón solicitado, esto lo hacemos con una función:

glAlphaFunc(int valor);
	    valor --> El valor minimo del alpha, si el Alpha es más bajo que 'valor' el objeto no se ve

Un ejemplo usando ALPHA_TEST:

glEnable(GL_ALPHA_TEST);  // Activa el Alpha test
	glAlphaFunc(10);   // Los objetos con Alpha 10 no se ven
 
	glPolyFmt(POLY_ALPHA(11) | POLY_ID(3) | POLY_CULL_NONE);
	glCallList(m1);  // El objeto se ve
 
	glPolyFmt(POLY_ALPHA(9) | POLY_ID(30) | POLY_CULL_NONE);
	glCallList(m2);  // El objeto no se ve

Bueno, ahí tenéis cómo usar las transparencias, con patrón o sin patrón, y con valores configurables con cada polígono y con 63 slots de transparencia distintos.

 

2- Usando la niebla, nuestro primer sistema de sombreado

Bien, este es el segundo "efecto especial" que vamos a usar, se trata de la niebla, un tipo de sombreado que oscurece o aclara una imagen en función de la cámara (posición del usuario). Esto lo vais a entender mejor con una imagen, a ver si os convencen los resultados:

 

 

Bien, si véis la imagen (hecha con NDS en 3D dual, ahora veremos cómo hacerlo), en la pantalla superior está el modelo de un desierto al cual le hemos aplicado niebla, y que, cuanto más lejos lo vemos, más atenuado está, y va atenuándose según la distancia de la cámara, es decir, lo que tengamos enfrente se ve normal, pero cuando más avanzamos más oscuro lo vemos.

Y en la imagen de la pantalla inferior vemos el mismo modelo del desierto pero sin niebla, como véis no queda bien...

Y con todo esto vamos a aprender a usar la niebla y configurarla (qué color tiene, dónde empieza, donde termina, la atenuación con la distancia etc...) todo con este pseudocódigo veremos cómo vamos a usar la niebla aquí:

void main(){
		Iniciar3D();
 
		CONFIGURAR NIEBLA
 
		while(1){
			Empieza3D();
 
			ACTIVAR NIEBLA
			DIBUJAR OBJETOS
			DESACTIVAR NIEBLA
 
			Actualiza3D();
		}
   }

Bien, como podemos ver en el código de arriba, lo primero que hay que hacer para usar la niebla es configurarla, para ello tenemos que hacer unas cuantas cosas:

- Decir la distancia entre una atenuación y otra

- Decir el color de la niebla

- Configurar la densidad de la niebla

- Decir dónde empieza la niebla

Bien, lo primero que hay que saber es que la NDS no es automática y hay que decirle a los 32 slots de niebla su densidad, que lo haremos de menor a mayor para lograr el efecto de la imagen anterior, pero antes debemos decir la distancia entre una densidad y otra, todo con esta función:

void glFogShift(int skip);
          Define la distancia entre un slot y otro
		  Cuanto mayor sea 'skip', mayor será la distancia
		  skip --> Distancia entre un slot y otro
 
	Ejemplo:
	   glFogShift(4);
	   La distancia entre un slot y otro será de 4 (int)

Después de esto tenemos que decir el color de la niebla (en la imagen anterior la niebla es de color negro, pero podemos poner el que queramos), todo con esta otra función:

void glFogColor(u8 r, u8 g, u8 b, u8 a);
	        Define el color de la niebla
			r --> Valor rojo de la niebla (0-31)
			g --> Valor verde (0-31)
			b --> Valor azul (0-31)
			a --> Valor Alpha (0-31)
	Ejemplo:
	    glFogColor(31, 0, 0, 31);  // La niebla es roja y opaca

Después tenemos que decirle a la NDS la densidad de cada uno de sus slots de niebla. Esto os va a sonar a rollo, porque tendremos que escribir 32 líneas para configurar la niebla, pero con un for() las cosas serán más fáciles. Para poner la densidad a un slot usamos esta función:

glFogDensity(int slot, int densidad);
	      Define la densidad de la niebla en uno de los 32 slots disponibles
		  slot ---> El slot a definir (0-31)
		  densidad --> La densidad de 'slot' (0-127)
	Ejemplo:
	   glFogDensity(3, 63);  // Aplica al slot 3 de niebla una densidad media
 
	Ejemplo 2, método más usado para poner a los 32 slots la niebla.
	   int slot, densidad;  // Variables que guardan el slot y la densidad
	   for(slot = 0, densidad = 0, slot < 32; slot ++){  // Empieza el bucle for
			densidad *= 4;  // Multiplica la densidad por 4 
			glFogDensity(slot, densidad);  // Aplica al slot la densidad calculada antes
	   }

Ya casi lo tenemos, lo último que falta es decir dónde empieza la niebla, todo con esta función:

void glFogOffset(int offset);
	      Define donde empieza la niebla
		  offset --> Valor donde empieza en hexadecimal (0x0000 - 0x8000)
 
	Ejemplos:
	     glFogOffset(0x7FFF);  // La niebla empieza muy atrás
		 glFogOffset(0x5000);  // La niebla empieza más cerca

Recordad que para usar la función glFogOffset() hay que saber hexadecimal, es decir, no vale poner cualquier número, si no os pasaréis de rango y os dará error.

Terminamos de configurar la niebla con un ejemplo:

// CONFIGURACION DE LA NIEBLA
 
	// DISTANCIA ENTRE UN SLOT Y OTRO
	glFogShift(3);  // 3 puntos entre slot
 
	// COLOR DE LA NIEBLA
	glFogColor(0, 0, 0, 31); // Color negro opaco
 
	// CONFIGURA LA DENSIDAD
	for(int slot = 0; slot < 32; slot ++){
		glFogDensity(slot, slot * 4);
	}
 
	// DI DONDE EMPIEZA
	glFogOffset(0x4000); // Más o menos en la mitad de la escena

 

Bien, todo este código que hemos aprendido es para configurar la niebla, pero ahora llega lo más fácil, activarla y desactivarla, os dejo un par de funciones que os ayudarán en ello:

void ActivaNiebla(void){
		glPolyFmt(POLY_ALPHA(31) | POLY_FOG | POLY_CULL_NONE);  // POLY_FOG hace que afecte la niebla a los poligonos
		glEnable(GL_FOG); // Activa la niebla
		// Dibujar objetos con niebla .......
	}
 
	void DesactivaNiebla(void){
		glPolyFmt(POLY_ALPHA(31) | POLY_CULL_NONE); // No ponemos POLY_FOG esta vez
		glDisable(GL_FOG);  // Desactiva la niebla
		// Dibujar objetos sin niebla ......
	}

Bueno, todo esto es lo que tenéis que saber para usar la niebla en la NDS, leedlo poco a poco, porque de golpe puede llegar a ser muy lioso, os recomiendo practicar con la niebla y las transparencias, jugad con los valores a ver qué pasa.

 

3- Usando 3D en ambas pantallas, una limitación menos

Llegamos al último apartado de este tutorial, en el que vamos a aprender a usar 3D en ambas pantallas.

Ahora que digo esto os preguntaréis: ¿Pero eso no era imposible? Bueno la NDS no puede usar 3D en ambas pantallas, pero en los ejemplos de Libnds hay un pequeño algoritmo para lograrlo, usando capturas de pantalla (capturando la escena 3D y dibujándola en la pantalla 2D). Lo que vamos a hacer es algo así:

int main(){
		Iniciar3D();
 
		Iniciar fondo inferior para capturas
 
		while(1){
			Empieza3D();
 
			Captura la pantalla y dibujala en la otra pantalla
 
			Dibujar superior
 
			Dibujar inferior
 
			Actualiza3D();
		}
	}

 

Tranquilos que ahora explico todo:

El sistema que vamos a usar el mediante capturas de pantalla, cojemos la escena 3D de la pantalla inferior (que está en la superior),  la capturamos y la imagen resultante la dibujamos en la otra pantalla (no estamos rompiendo las reglas, sólo capturamos la escena 3D y la dibujamos en la pantalla 2D). Y luego con una variable BOOL sabremos cuándo tenemos que dibujar en la superior y cuándo en la inferior.

Esto de usar el 3D dual tiene un inconveniente:

El juego te va a ir a 30FPS (frames por segundo) como máximo, debido a que lo máximo son 60FPS, y lo que hacemos es dibujar 3D en una pantalla en un frame y en el siguiente dibujamos la otra, por lo que como máximo te irá a esa velocidad.


Lo primero que tenemos que hacer es iniciar la pantalla 2D para hacer capturas, todo con esta función:

// Modo de video BITMAP
REG_DISPCNT_SUB = MODE_5_2D;
 
// Inicia el fondo 3 inferior (BITMAP) para la imagen
bgInitSub(3, BgType_Bmp16, BgSize_B16_256x256, 0, 0);
 
// Inicia el banco D de VRAM para guardar la captura
vramSetBankD(VRAM_D_LCD);
 
// Inicia el banco C de VRAM para mostrar la imagen
vramSetBankC(VRAM_C_SUB_BG);

Bien, lo que viene en este código es para iniciar la pantalla 2D, con ello, como en 3D, ponemos un modo de vídeo para mostrar la imagen (en este caso el modo Bitmap 16bits (máxima calidad)), indicamos el fondo en la capa 3 e iniciamos los bancos C y D (C para mostrar la imagen y D para guardar la captura (Gracias a NightFox por aclarar esto)).

Bien, ahora que hemos iniciado la pantalla inferior para las capturas, tenemos que usar esta función en el bucle while(1), ésta se encargará de controlar las pantallas y de realizar las capturas, y de decirte en qué pantalla se está dibujando actualmente:

// Variable donde guardamos la pantalla principal (donde se dibuja el 3D)
bool mainscreen = true;
 
// Operaciones del modo dual 3D
void ModoDual3D(void){
 
        // Manda la captura del banco D al C (pantalla bitmap)
	// Para que sea más rápido hazlo por DMA, canal 3
	while(dmaBusy(3));   // Espera a que esté libre
	DC_FlushRange((void*)0x06860000, 98304);  // Reserva espacio en caché para la copia
	dmaCopyHalfWords(3, (void*)0x06860000, (void*)0x6200000, 98304); // Haz la copia por DMA (16 bits)
	DC_InvalidateRange((void*)0x06860000, 98304);  // Evita que se almacenen en caché
 
	// Espera a que el registro de capturas este libre
	while(REG_DISPCAPCNT &DCAP_ENABLE);
 
	// Captura el frame actual en el banco D
    REG_DISPCAPCNT = DCAP_BANK(3)| DCAP_ENABLE | DCAP_SIZE(3);
 
	// Cambia la pantalla principal
	mainscreen = !mainscreen;
 
	if(mainscreen) lcdMainOnBottom();  // Pantalla inferior
	else lcdMainOnTop();    // Pantalla superior
 
}

Esta función sí que hay que explicarla, primero copiamos el contenido del banco D al banco C (gráficos pantalla inferior) para mostrar la imagen. Y luego cambiamos la pantalla principal en la variable "mainscreen" . Según la pantalla que sea, configuramos la VRAM y realizamos la captura y la ponemos en la pantalla inferior que definimos antes.

Bien, este es el código completo para mostrar una pantalla 3D dual:

int main(){
		Iniciar3D();
 
		// Inicia la pantalla inferior para dibujar la captura
		IniciarCapturas();
 
		while(1){
			Empieza3D();
 
			// Realiza las operaciones del modo 3D dual
			ModoDual3D();
 
			// Dibuja la escena dual
			if(mainscreen){ 
				// Dibujamos en la pantalla inferior
			}else{
				// Dibujamos en la pantalla superior
			}	
 
			Actualiza3D();
		}
	}

 

Muy bien, con estas 2 funciones que he dado y con el pseudocódigo de arriba seréis capaces de dibujar en ambas pantallas. Ahora pondré un par de ejercicios para asimilar todo lo aprendido, y recomiendo releer este tutorial , ya que es largo y con muchas cosas, si tenéis alguna duda ponedla en los comentarios.

 

Ejercicios

Ejercicio 8

En este vamos a usar las transparencias que admite la NDS (sin usar el Alpha_Test), y jugaremos con el Alpha de los 3 modelos que tenemos.

Arriba-Abajo --> Transparencia del objeto seleccionado
Derecha-Izquierda --> Seleccionar objeto
LR --> Zoom

http://www.mediafire.com/?9cq4b9nqnmbmmbh

 

Ejercicio 9

En este vamos a usar la niebla y el 3D en ambas pantallas, mostraremos el modelo de un desierto en la pantalla superior con niebla y en la inferior sin niebla, para poder ver la enorme diferencia entre ambos.

Más adelante veremos el "toon shading", otro modo de sombreado que es bueno dárselos a los personajes, pero
eso lo veremos más adelante...

Pad direccional --> Mover la cámara en ambas pantallas
LR --> Girar el ojo X de la cámara

http://www.mediafire.com/?weqpgffd5kc686d

 

Bueno, este es el fin de este tutorial, en el siguiente veremos los entornos (cube maps) y el toon shading.

Hasta entonces, saludos!

 


~Actualmente estudiando Ingeniería de las Tecnologías de la Telecomunicación en la Escuela de Ingenieros~


Anuncios Google

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.

Otra vez, felicitaciones, que

Otra vez, felicitaciones, que tal trabajote que te esta dando.

 

Imagen de Andresmargar

^^

Pues todavía quedan 2 más xD

Imagen de kNightFox

Otra opcion mas "rapida" para

Otra opcion mas "rapida" para dual 3d seria esta, usando un banco en modo LCD para guardar la captura, asi te ahorras procesar toda la malla de sprites:

http://pastebin.com/FGjZZN4B  

El codigo es el que use en los creditos de mind maze y ya sabes lo bien que quedo ^^


Nuestra web oficial:
http://www.nightfoxandco.com/
 
Siguenos en facebook:
http://www.facebook.com/pages/NightFox-Co/284338634917917
 
Por favor, no useis los MP para preguntas, usar el FORO:
http://www.nightfoxandco.com/forum/
Asi nos ahorramos de contestar lo mismo 20 veces.

 

Imagen de Andresmargar

Muy bueno ^^

Muy bueno, encima le puedes añadir el VBlank, no como el mío sacado de los ejemplos de Libnds xD

Cuando tenga un momento adapto el código al ejemplo y lo publico en el tutorial.

Gracias ;)

 

EDIT: Ya lo he adaptado y va de lujo, solo que la imagen va algo lenta, pero creo que será porque estoy copiando el VRAM_D al VRAM_C con el memcpy(), voy a probar con DMA a ver si sale más rápida.

 

EDIT2: He probado con DMA y ya va bien, incluso mejor que antes ^^


~Actualmente estudiando Ingeniería de las Tecnologías de la Telecomunicación en la Escuela de Ingenieros~

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.