[Tutorial] III.9 - Buffer de profundidad (DEPTH) y modo tijera (SCISSOR)

Ver el tema anterior Ver el tema siguiente Ir abajo

[Tutorial] III.9 - Buffer de profundidad (DEPTH) y modo tijera (SCISSOR)

Mensaje  HarZe el Mar Ago 04, 2009 1:00 pm

III.9 - Buffer de profundidad (DEPTH) y modo tijera (SCISSOR)
Novedades:
  • Uso de GL_DEPTH_TEST en glEnable()
  • Uso de GL_SCISSOR_TEST en glEnable()
  • Uso de nuevo buffer: GLUT_DEPTH en glutInitDisplayMode()
  • glDepthRange()
  • glDepthFunc()
  • glScissor()

La profundidad es algo realmente básico en el 3D, no tanto en el 2D. Pasaremos al 3D en el bloque IV, pero antes quiero inciarlo aquí. El buffer color guardaba el color de cada pixel que se mostraría en pantalla, y ahora tenemos dos de color con GLUT_DOUBLE. Añadamos un buffer más a nuestra lista, el GLUT_DEPTH, es un buffer aparte que guarda, pixel a pixel, a cuánta profundidad (en el eje Z) está el último pixel de color dibujado. Como siempre, es mejor ver primero el ejemplo, que es muy sencillo. (El buffer de profundidad es algo que se puede usar sin entenderlo, pero recomiendo entender todo lo que haces).
Código:
#include <GL/glut.h>

int tijera, profundidad;
int ventana[2];

void IniciarGLUT() {
   glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
   glutInitWindowSize(600,600);
   glutInitWindowPosition(100,100);
   glutCreateWindow("Practica III,9 de OpenGL");

   glDepthFunc(GL_LEQUAL);
   glDepthRange(-1,1);
}

void PintarEscena() {
   glMatrixMode(GL_MODELVIEW);
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
   glLoadIdentity();

   if (tijera) glEnable(GL_SCISSOR_TEST);
   if (profundidad) glEnable(GL_DEPTH_TEST);
   glScissor(100,100,ventana[0]-200,ventana[1]-200);

   float i;
   glBegin(GL_QUADS);
      for (i=-10; i<9; i++) {
         glColor3f((i+10)/20,0,0);
         glVertex3i(i,i,-i);
         glVertex3i(i+2,i,-i);
         glVertex3i(i+2,i+2,-i);
         glVertex3i(i,i+2,-i);
      }
   glEnd();

   glDisable(GL_DEPTH_TEST);
   glDisable(GL_SCISSOR_TEST);
   
   glutSwapBuffers();
}

void ReProyectar(int w, int h) {
   GLfloat formato;

   ventana[0] = w;
   ventana[1] = h;

   if(h == 0) h = 1;
   
   glViewport(0, 0, w, h);

   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();

   formato = (GLfloat)w / (GLfloat)h;
   if (w <= h) glOrtho (-10.0f, 10.0f, -10.0f / formato, 10.0f / formato, -10.0f, 10.0f);
   else glOrtho (-10.0f * formato, 10.0f * formato, -10.0f, 10.0f, -10.0f, 10.0f);
}

void Teclado(unsigned char key, int x, int y) {
   switch (key) {
      case 'z': profundidad = !profundidad; break;
      case 't': tijera = !tijera; break;
   }
}

int main(int argc, char **argv) {
   glutInit(&argc,argv); //Solo necesario en Linux
   IniciarGLUT();
 
   glutReshapeFunc(ReProyectar);
   glutDisplayFunc(PintarEscena);
   glutIdleFunc(PintarEscena);
   glutKeyboardFunc(Teclado);
 
   glutMainLoop();
   return 0;
}

Esta es la salida del programa si se aplica la profundidad y el test de tijera.

Por partes, primero hay que explicar el buffer de profundidad, para ello uso estos cuadrados superpuestos:
Código:
   glBegin(GL_QUADS);
      for (i=-10; i<9; i++) {
         glColor3f((i+10)/20,0,0);
         glVertex3i(i,i,-i);
         glVertex3i(i+2,i,-i);
         glVertex3i(i+2,i+2,-i);
         glVertex3i(i,i+2,-i);
      }
   glEnd();
Sin el buffer de profundidad, al pintar sobre una zona ya pintada, esta se repinta con lo que nosotros digamos. Pero si activamos el buffer de profundidad, y aquellos que queremos dibujar después de lo anterior está por detrás, y entonces no se debería ver, no se dibuja. Por poner un ejemplo real, si tu en juego mandas dibujar una ciudad entera en todo momento, y me metes dentro de un edificio, no verás la ciudad, porque está por detrás de la pared, así no solo ahorras cálculos de dibujado sino que consigues el realismo que buscas.
El funcionamiento real del buffer es: cada vez que dibujas algo, se calcula en que pixeles se dibujaría, y se realiza un test, si este dibujo nuevo tiene un valor en Z menor o igual que el de la última figura dibujada, se verá, si está más lejos, el valor es mayor en Z, y por lo tanto no se vería. Tan simple como eso.
Ahora veamos el código para tenerlo más claro, primero veamos las novedades en IniciarGLUT():
Código:
   glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
        ....
   glDepthFunc(GL_LEQUAL);
   glDepthRange(-1,1);
Añadimos un nuevo buffer, y después especificamos un par de cosas. Primero glDepthFunc(GL_LEQUAL); que indica que el test de profundidad se guía por la regla GL_LEQUAL. Es decir, a cada nueva figura a dibujar se le hace un test, si lo pasa, se dibuja, y no lo pasa y falla, no se dibuja. Con GL_LEQUAL decimos que pasarán el test todos aquellos datos cuyos valores en Z sean menores o iguales al presente en ese pixel, esto es lo normal. Podemos hacer que siempre pase el test y todo se dibuje (GL_ALWAYS) o que nunca pase el test (GL_NEVER), también conseguir extraños resultados con GL_GEQUAL (solo pasa cuando algo es igual o mayor en Z, es decir, está detrás), la antiprofunidad, y muy extraños efectos. El natural es GL_LEQUAL.
En cuanto a glDepthRange(-1,1), simplemente sirve para cambiar (si quieres) el rango de datos de profundidad normalizada en la que deseas trabajar (la coordenadas normalizadas quiere decir que OpenGL trabaja con una distancia en Z de -1 como mínimo a 1 como máximo, pasando por todos los decimales, por defecto). De momento déjalo así.

Dentro de la función PintarEscena(), simplemente hacemos que se active GL_DEPTH_TEST si lo activas o no con la letra Z para que veas la diferencia. Los cuadrados que se dibujan, si no se activa la profundidad, se dibujan uno sobre el otro, empezando por la izquierda abajo hasta la derecha arriba, por lo que el último cuadrado de arriba a la derecha se superpone al anterior a pesar de estar más lejos en Z. Esto camiará cuando activemos la profundidad, ya dará igual cuando lo mandes pintar, como está por detrás solo se pintará la parte que no vaya a ser tapada por el anterior cuadrado.

Solo queda explicar el modo tijera, GL_SCISSOR_TEST se activará pulsando T, si hacemos esto se reducirá la zona donde se pintará según indiquemos con glScissor(100,100,ventana[0]-200,ventana[1]-200);. Indicando el punto desde el que empieza la zona que si es dibujable, y luego indicando el ancho y alto de esa zona. Si ya tienes práctica, te preguntarás que direfencia tiene esto con glViewport, pues muy sencillo, aunque dificil de explicar, pero lo intentaré. imagina que tienes un espacio que empieza en 0 en X e Y, y acaba en 5 en X e Y. Y mandas pintar un cuadrado en el centro de 1 de longitud de lado. Si aplicas glViewport()[i] para que no se pinte ni arriba ni abajo (como si vieses una peli panorámica en una pantalla cuadrada), las dimensiones de 5x5 del espacio virtual se moldearán al nuevo Viewport, y se achatará, y con el, el cuadrado resultante. Pero, si usas la tijera para que no se vea la zona de arriba ni de abajo, solo haces que no se vea de 0 a 1 en Y y de 4 a 5 en Y, pero no se achata, porque solo has recortado el dibujo resultante. Lo mejor es emperimentar, como siempre. Aparte de para esto, la tijera, se suele usar para ahorrar cálculos, mandando cortar justo en el borde de la ventana, tú no lo notas, pero evitas que se dibuje en una zona donde nada se llegaría a ver nunca, porque se sale de la pantalla. Como si tratas de ver un triángulo que no cabe en pantalla, se calcula el triángulo entero, y se manda dibujar (si se puede) entero, con la tijera no hace falta ni comprobar si hay algo que pintar fuera de la ventana, porque ya se deja claro que no. Asi que desde ahora, al igual que se hacia [i]glViewport(0,0,w,h), recomiendo añadir a tu función de Reshape un: glScissor(0,0,w,h).

Una cosa más: glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT), ahora tienes dos bufferes que limpiar cada vez que re-pintes. O aquello empezará a sumarse y sería un caos, como si no limpias el buffer de color antes de pintar de nuevo la escena.

HarZe
WebMaster & Desarrollador

Cantidad de envíos : 58
Fecha de inscripción : 21/06/2009
Edad : 24

Ver perfil de usuario http://opengl-esp.superforo.net

Volver arriba Ir abajo

Re: [Tutorial] III.9 - Buffer de profundidad (DEPTH) y modo tijera (SCISSOR)

Mensaje  nosek159 el Mar Ene 04, 2011 1:00 pm

Buenas,

Primero que nada queria agradecerte por estos tutoriales, son muy buenos y estan muy bien explicados por eso te animo a seguir con los tutos y a pasar al 3d, quiero 3d!! xD


Saludos,
Dani.

nosek159

Cantidad de envíos : 1
Fecha de inscripción : 23/12/2010

Ver perfil de usuario

Volver arriba Ir abajo

Ver el tema anterior Ver el tema siguiente Volver arriba

- Temas similares

 
Permisos de este foro:
No puedes responder a temas en este foro.