Pues sí, aún hay más. Ya vimos anteriormente los buffers de color y de profundidad, pues aún nos quedan otros dos más, el "Buffer de Estarcido" y el "Buffer de Acumulación".
El buffer de estarcido es muy útil para limitar zonas espaciales donde poder dibujar. Como OpenGL no proyecta sombras, las tenemos que dibujar nosotros y si no tenemos cuidado podemos dibujar una sombra fuera del suelo u a travesando una pared.
Así que dibujando primero el suelo en el buffer de estarcido y en el de color, podemos crear un área espacial donde sólo en ella podamos dibujar las sombras. Luego dibujamos las sombras proyectadas por los objetos verificando que estén dentro del área espacial que delimita el buffer de estarcido. Y por último pintamos los objetos que proyectan las sombras.
// -- HACER LA MÁSCARA DEL SUELO -- glClearStencil( 0 ); glClear( GL_STENCIL_BUFFER_BIT ); glDisable( GL_DEPTH_TEST ); glEnable( GL_STENCIL_TEST ); glStencilFunc( GL_ALWAYS, 1, 1 ); glStencilOp( GL_KEEP, GL_KEEP, GL_REPLACE ); // -- DIBUJAR EL SUELO [ FALTA CÓDIGO ]-- // -- TERMINAR LA MASCARA DEL SUELO -- glStencilFunc( GL_EQUAL, 1, 1 ); glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP ); // -- APAGAR LA LUZ Y ACTIVAR LAS TRANSPARENCIAS -- glPushAttrib( GL_LIGHTING_BIT ); glDisable( GL_LIGHTING ); glEnable( GL_BLEND ); // -- DIBUJAR LAS SOMBRAS [ FALTA CÓDIGO ] -- // -- DESACTIVAR LAS TRANSPARENCIAS Y ENCENDER LA LUZ -- glDisable( GL_BLEND ); glPopAttrib(); glEnable( GL_DEPTH_TEST ); glDisable( GL_STENCIL_TEST ); // -- DIBUJAR LOS OBJETOS QUE PROYECTAN SOMBRAS [ FALTA CÓDIGO ] --
glStencilMask( 1 ); // Permite escribir en el Stencil Buffer. glStencilMask( 0 ); // No permite escribir en el Stencil Buffer. glClearStencil( GLint s ); // Valor para limpiar el Stencil Buffer. glClear( GL_STENCIL_BUFFER_BIT ); // Limpia el Stencil Buffer. glEnable( GL_STENCIL_TEST ); // Activa el Stencil Buffer. glDisable( GL_STENCIL_TEST ); // Desactiva el Stencil Buffer. glStencilFunc( GLenum funcion, GLint referencia, GLuint mascara ); funcion: - GL_NEVER Siempre falla (no se dibuja). - GL_LESS Pasa si ( referencia & mascara ) < ( estarcido & mascara ) - GL_EQUAL Pasa si ( referencia & mascara ) == ( estarcido & mascara ) - GL_LEQUAL Pasa si ( referencia & mascara ) <= ( estarcido & mascara ) - GL_GREATER Pasa si ( referencia & mascara ) > ( estarcido & mascara ) - GL_NOTEQUAL Pasa si ( referencia & mascara ) != ( estarcido & mascara ) - GL_GEQUAL Pasa si ( referencia & mascara ) >= ( estarcido & mascara ) - GL_ALWAYS Siempre pasa (siempre dibuja). glStencilOp( GLenum fallo, GLenum zfallo, GLenum zpasa ); fallo: Si Stencil Falla. zfallo: Si Stencil Pasa y Depth Test Falla. zpasa: Si Stencil Pasa y Depth Test Pasa. funciones para los tres: - GL_KEEP No modifica el buffer de estarcido. - GL_ZERO Pone cero en el buffer de estarcido. - GL_REPLACE Pone el valor de referencia en el estarcido. - GL_INCR Incrementa el valor actual del buffer de estarcido. - GL_DECR Decrementa el valor actual del buffer de estarcido. - GL_INVERT Invierte el orden binario del buffer estarcido.
El buffer de acumulación sirve para capturar el buffer de color en un fotograma y aplicarle acumulaciones del color de otro fotograma/s. Esto básicamente sirve para simular el efecto de estela que deja un objeto cuando se mueve muy rápidamente en el encuadre de la cámara. También tiene alguna que otra aplicación pero en mis pruebas ralentiza demasiado y por eso no lo usaremos ni explicaré ejemplo alguno. Ver tabla inferior.
glAccum( GLenum func, GLfloat valor ); funcion: - GL_LOAD Copia el buffer de color al buffer de acumulación. - GL_RETURN Copia el buffer de acumulación al buffer de color. - GL_ACCUM Añade valores de color escalados al buffer de acumulación. - GL_ADD Añade un color constante a los valores del buffer de acumulación. - GL_MULT Multiplica el buffer de acumulación por un color dado.
El truco consiste en dibujar con la luz apaga la proyección de un objeto transparente en el suelo respecto a un punto de luz. Para ello necesitamos tres puntos del suelo, un objeto y la posición de la luz.
Con los tres puntos del suelo se calcula la ecuación del plano. Ahora se hace el producto mixto de la ecuación del plano por la posición del punto de luz y se calcula la matriz de la proyección de la sombra. Se multiplica la matriz de modelado por la matiz de la proyección de la sombra "glMultMatrixf( (const GLfloat *) Matriz_Sombra );" y se dibuja el objeto.
Ahora imagínate que tienes ocho luces, el suelo no es llano y quieres que los objetos se hagan sombra entre sí... La cantidad de cálculos sería impresionante para hacerlos en tiempo real, así que no te emociones mucho con las sombras. Normalmente vasta pintar dos o tres sombras simples para engañar al espectador y punto pelota. Empezamos por calcular la ecuación del plano:
// ----------------------------------------------------------- // -- Cálculo de la Ecuación del Plano ----------------------- // ----------------------------------------------------------- // -- Tres Puntos -> GLfloat Puntos[9], GLfloat *Plano[4] ---- // ----------------------------------------------------------- void Ecuacion_del_Plano( GLfloat *Puntos, GLfloat *Plano ) { GLfloat longitud; GLfloat v1[3], v2[3], v3[3]; v1[0] = Puntos[3] - Puntos[0]; v1[1] = Puntos[4] - Puntos[1]; v1[2] = Puntos[5] - Puntos[2]; v2[0] = Puntos[6] - Puntos[3]; v2[1] = Puntos[7] - Puntos[4]; v2[2] = Puntos[8] - Puntos[5]; v3[0] = ( v1[1] * v2[2] ) - ( v1[2] * v2[1] ); v3[1] = ( v1[2] * v2[0] ) - ( v1[0] * v2[2] ); v3[2] = ( v1[0] * v2[1] ) - ( v1[1] * v2[0] ); longitud = (GLfloat)sqrt( (v3[0]*v3[0]) + (v3[1]*v3[1]) + (v3[2]*v3[2]) ); if( longitud == 0.0f ) longitud = 1.0f; v3[0] /= longitud; v3[1] /= longitud; v3[2] /= longitud; Plano[0] = v3[0]; Plano[1] = v3[1]; Plano[2] = v3[2]; Plano[3] = - ( ( Plano[0] * Puntos[6] ) + ( Plano[1] * Puntos[7] ) + ( Plano[2] * Puntos[8] ) ); }
Dibujamos el suelo ( con ya sabemos con el stencil buffer ) y calculamos la matriz de proyección de la sombra.
// ----------------------------------------------------------------------------- // -- Cálculo de la Matriz de Sombreado ---------------------------------------- // ----------------------------------------------------------------------------- // GLfloat Plano[4], GLfloat Luz_Pos[4] // ----------------------------------------------------------------------------- const void Hacer_Sombra( GLfloat *Plano, GLfloat *Luz_Pos ) { GLfloat Dot; GLfloat Matriz_Sombra[4][4]; // -- Producto mixto del plano y la posición de la luz -- Dot = ( Plano[0] * Luz_Pos[0] ) + ( Plano[1] * Luz_Pos[1] ) + ( Plano[2] * Luz_Pos[2] ) + ( Plano[3] * Luz_Pos[3] ); // -- Ahora hace la proyección -------------------- Matriz_Sombra[0][0] = Dot - Luz_Pos[0] * Plano[0]; Matriz_Sombra[1][0] = 0.0f - Luz_Pos[0] * Plano[1]; Matriz_Sombra[2][0] = 0.0f - Luz_Pos[0] * Plano[2]; Matriz_Sombra[3][0] = 0.0f - Luz_Pos[0] * Plano[3]; Matriz_Sombra[0][1] = 0.0f - Luz_Pos[1] * Plano[0]; Matriz_Sombra[1][1] = Dot - Luz_Pos[1] * Plano[1]; Matriz_Sombra[2][1] = 0.0f - Luz_Pos[1] * Plano[2]; Matriz_Sombra[3][1] = 0.0f - Luz_Pos[1] * Plano[3]; Matriz_Sombra[0][2] = 0.0f - Luz_Pos[2] * Plano[0]; Matriz_Sombra[1][2] = 0.0f - Luz_Pos[2] * Plano[1]; Matriz_Sombra[2][2] = Dot - Luz_Pos[2] * Plano[2]; Matriz_Sombra[3][2] = 0.0f - Luz_Pos[2] * Plano[3]; Matriz_Sombra[0][3] = 0.0f - Luz_Pos[3] * Plano[0]; Matriz_Sombra[1][3] = 0.0f - Luz_Pos[3] * Plano[1]; Matriz_Sombra[2][3] = 0.0f - Luz_Pos[3] * Plano[2]; Matriz_Sombra[3][3] = Dot - Luz_Pos[3] * Plano[3]; glPushAttrib( GL_LIGHTING_BIT ); // Guardamos la luz. glDisable( GL_LIGHTING ); // Apagamos la luz. glPushMatrix(); // Guardamos la matriz. glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable( GL_BLEND ); // Activamos las transparencias. glColor4f( 0.0, 0.0, 0.0, 0.2 ); // -- Ahora Multiplica la Matriz -------------------- glMultMatrixf( (const GLfloat *) Matriz_Sombra ); }
Ahora dibujamos los objetos que proyecten sombra y usamos la función de abajo para restaurar diversas configuraciones.
const void Terminar_Sombra( void ) { glDisable( GL_BLEND ); // Quita las transparencias. glPopMatrix(); // Repone la proyección a normal glPopAttrib(); // Repone las iluminación }
Volvemos a dibujar los objetos que proyecten sombra y se acabó lo que se daba.
Usaremos los Buffers de Color, Profundidad y Estarcido. Hay otro llamado Buffer de Acumulación pero por mis pruebas no es válido para renderizar en tiempo real puesto que ralentiza demasiado.
El Buffer de Color es donde se dibuja, conviene vaciarlo con un color antes de dibujar el siguiente cuadro.
El Buffer de Profundidad sirve para no dibujar objetos delante de otros cuando no debe. Si primero dibujamos algo cerca de la cámara y después algo lejos, pues sin este buffer se dibujaría el objeto lejano encima del cercano.
Es Buffer de Estarcido vale para limitar la zona de dibujado. Esto es útil para dibujar agujeros en objetos, hacer cortinillas o limitar el dibujado de sombras y reflejos.
Descargar Practica nº09 (Solo usuarios registrados)
Dibuja la sombra de un objeto proyectada en el suelo por dos luces. Una direccional y la otra Puntual. Usa la Ecuación del Plano y STENCIL_BUFFER. Y para complicar un poco más la cosa, que las luces y el objeto estén en movimiento. Haz también su reflejo sobre el suelo.
Descargar Practica nº10 (Solo usuarios registrados)