Reflexiones con y sin Stencil Buffer.
1. Primera parte de Stencil Buffer.
Esta es la continuación de uno de mis trabajos de informática gráfica de la Universidad Complutense de Madrid, al final tengo el código que podéis compilar en cualquier compilador C++, aunque previamente tendréis que averiguar como instalar las librerías necesarias. Mi codigo esta compilado con el C++Builder en modo consola.
Sin stencil.
El algoritmo de creación de reflexiones sin Stencil es muy simple. El algoritmo de la reflexión mas simple es cuando el espejo esta en cualquiera de los planos XY, XZ, YZ. Simplemente dibujamos otro objeto idéntico detrás de este plano transparente. El algoritmo es el siguiente:
1. Cargamos la matriz de modelado y damos las coordenadas de la vista:
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt( 0.0f, 3.0f, 7.0f,
0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f);
2. Metemos en la pila matriz de modelado:
glPushMatrix();
3. Multiplicamos la siguiente matriz de modelado por la matriz de reflejos a través de plano XZ, que lo único que hace es negar las coordenadas Y.
glScalef(1.0, -1.0, 1.0);
Si fuera otro plano aleatorio deberíamos de calcular la matriz nosotros mismos y después multiplicar la matriz de modelado por esta matriz. El algoritmo de cálculo de la matriz (4 x 4) a partir del punto del plano y su normal es el siguiente:
void mirrorMatrix(GLfloat& m[4][4], // Matriz resultante.
GLfloat p[3], // Punto del plano.
GLfloat v[3]) // Vector perpendicular al plano.
{
GLfloat dot = p[0]*v[0] + p[1]*v[1] + p[2]*v[2];
m[0][0] = 1 – 2*v[0]*v[0];
m[1][0] = – 2*v[0]*v[1];
m[2][0] = – 2*v[0]*v[2];
m[3][0] = 2*dot*v[0];
m[0][1] = – 2*v[1]*v[0];
m[1][1] = 1 – 2*v[1]*v[1];
m[2][1] = – 2*v[1]*v[2];
m[3][1] = 2*dot*v[1];
m[0][2] = – 2*v[2]*v[0];
m[1][2] = – 2*v[2]*v[1];
m[2][2] = 1 – 2*v[2]*v[2];
m[3][2] = 2*dot*v[2];
m[0][3] = 0;
m[1][3] = 0;
m[2][3] = 0;
m[3][3] = 1;
}
Después lo único que tendríamos hacer es multiplicar la matriz de modelado por esta matriz.
glMultMatrixf(&matriz[0][0]); //Le pasamos dirección de nuestra matriz.
4. Si dibujamos caras traseras tenemos que dibujar caras frontales.
glCullFace(GL_FRONT);
5. Dibujamos nuestra escena debajo del plano XZ:
dibujaEsfera();
6. Reestablecemos el modo de dibujado de caras y retiramos la matriz de modelado.
glCullFace(GL_BACK);
glPopMatrix();
7. Dibujamos el plano transparente entre las dos esferas:
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE);
dibujaSuelo();
glDisable(GL_BLEND);
8. Finalmente dibujamos nuestra escena.
dibujaEsfera();
Las limitaciones de este sistema son evidentes. Tenemos que limitar nuestra vista para que no mire detrás de nuestro espejo. Otra limitación es pensar que ocurriría si tenemos más de un espejo. Solo lo podríamos usar bien en planos infinitos que no siempre son posibles. El resultado es este:
Con stencil.
Con stencil el algoritmo descrito anteriormente casi no cambia los pasos. La diferencia crucial es que etiquetamos cada espejo con un identificador. Cuando una reflexión es renderizada el algoritmo lo que hace es actualizar los píxeles que coincidan con el identificador de la superficie. Para actualizar el algoritmo lo primero que tenemos que hacer es crear una ventana que soporte Stencil Buffer. En el bucle principal tenemos que limpiar los buffers y renderizár la escena con stencil buffer desabilitado.
glClearStencil(0);
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glEnable(GL_DEPTH_BUFFER_BIT);
glDisable(GL_STENCIL_TEST);
dibujaEscena();
Despues para cada espejo tenemos que seguir estos pasos:
1. Crear el buffer con el valor 1, siempre cuando el buffer de prifundidad es pasado. Desabilitamos escritura en el buffer del color y finalmente dibujamos el espejo.
glEnable(GL_STENCIL_TEST);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
glStencilFunc(GL_ALWAYS, 1, ~0);
glColorMask(0,0,0,0);
dibujaSuelo();
Este paso etiqueta cada espejo con valor 1, el test de profundidad es para que los pixels no visibles no sean etiquetados.
2. Con el “color buffer” desabilitado , establecemos un rango de profundidad para escribir los valores mas lejanos posibles para todos los pixeles actualizados y que buffer de profundidad siempre pase. Establecemos stencil que solo actualize pixels etiquetados con valor 1. Dibujamos los poligonos del espejo.
glDepthRange(1,1);
glDepthFunc(GL_ALWAYS);
glStencilFunc(GL_EQUAL,1 , ~0);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
dibujaSuelo();
Este paso reajusta buffer de profundidad a su maximo valor por defecto para todos los pixeles visibles.
3. Restauramos profundidad y color para sus valores estándar.
glDepthFunc(GL_LESS);
glColorMask(1,1,1,1);
glDepthRange(0,1);
Ahora ya estamos listos para renderizar la reflexion en si. Los pixeles que pertenecen a la reflexion son etiquetados con el valor 1, pero al mismo tiempo tienen el maximo valor de profundidad. La operación GL_LESS (menor que test de profundidad) asegura el correcto dibujado de la escena (Hace que la reflexion no se vea).
4. Ahora tenemos que dibujar la escena al otro lado del plano, Para el ejemplo solo negare la coordenada Y porque el espejo esta en el plano XZ:
glPushMatrix();
glScalef(1.0, -1.0, 1.0);
dibujaEsfera();
glPopMatrix();
Basicamente el algoritmo es este aunque cambia si queremos añadir mas espejos. Yo añadí al final el renderizado del espejo en si con una función de transparencia porque, no se veía el espejo en si, solo el reflejo.
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE);
dibujaSuelo();
glDisable(GL_BLEND);
El resultado de los dos algoritmos es el siguiente, en la primera imagen se ve claramente que debajo del plano simplemente hay otra esfera. La imagen de la derecha se parece mas a un reflejo de verda aunque no muy conseguido, ya que no tiene texturas:
Aqui teneis codigos de ambos ejemplos en c++, tambien viene una carpeta con librerias, sinceramente ya no me acuerdo como instalarlo y menos si se podria instalar en compliladores que no son C++Builder. Seguramente para DevC++ o GCC hay manuales a parte:



