Para ello necesitaremos guardar en una textura la profundidad lineal de la escena completa, para ello renderizaremos toda la escena (no hace falta aplicar texturas ni iluminación para este paso) desde el punto de vista de la cámara. Para ello usaremos Framebuffers con el DepthTest habilitado y usaremos, por ejemplo, los siguientes shaders:
//Vertex Shader public static string SSAO01DepthVS = @" varying vec4 v_position; void main() { gl_Position = ftransform(); //view space position v_position = gl_ModelViewMatrix * gl_Vertex; }"; //Fragment Shader public static string SSAO01DepthPS = @" varying vec4 v_position; uniform float cameraFarPlane; void main() { float depth = v_position.z / cameraFarPlane; depth = depth * 0.5 + 0.5; //Don't forget to move away from unit cube ([-1,1]) to [0,1] coordinate system gl_FragColor = vec4(depth, 0.0, 0.0, 0.0); }";
Ahora necesitaremos pasarle al Shader encargado de calcular la oclusión esta profundidad renderizando un Quad que ocupe toda la pantalla.
El shader lo que hará es:
1.- Mirar la profundidad del píxel actual.
2.- Compararla con la profundidad de los pixeles que tenga alrededor.
3.- Decidir el factor de oclusión a aplicar.
Para ello se han preparado unos parámetros que tendremos que rellenar:
//fragment increment(1.0/width, 1.0/height) GL.Uniform2(GL.GetUniformLocation(xoShaderSSAO01Occlusion.Program, "texIncr"), 1f / (float)this.Width, 1f / (float)this.Height); //linear zbuffer: uniform sampler2D tex0; BindTexture(xoAOColorTexture.TextureId, TextureUnit.Texture0, "tex0", xoShaderSSAO01Occlusion.Program); //samples to take GL.Uniform1(GL.GetUniformLocation(xoShaderSSAO01Occlusion.Program, "iterations"), 3); //increment between samples GL.Uniform1(GL.GetUniformLocation(xoShaderSSAO01Occlusion.Program, "samIncr"), 1f); //sqrt(max occlusion) GL.Uniform1(GL.GetUniformLocation(xoShaderSSAO01Occlusion.Program, "maxOcc"), (float)System.Math.Sqrt(0.9));
En texIncr insertaremos el incremento en las coordenadas de texturas para poder navegar por ellas.
En tex0 introduciremos la textura de profundidad.
En iterations el número de veces que queremos que haga el sampleado de píxeles (cuanto más alto este número, mas se notará el efecto pero tardaremos más en realizar el efecto y bajarán los FPS).
En samIncr introduciremos el incremento de espacio entre iteraciones.
Y en maxOcc introduciremos la occlusion máxima que sufrirá un píxel, siendo 1.0 el máximo, pero aplicándole la raiz cuadrada para ahorrarle una operación al shader.
Los shaders para este proceso son:
//Vertex Shader public static string SSAO02OcclusionVS = @"#version 120 void main() { gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; gl_TexCoord[0] = gl_MultiTexCoord0; }"; //Fragment Shader public static string SSAO01OcclusionPS = @"#version 120 //linear zbuffer uniform sampler2D tex0; //fragment increment(1.0/width, 1.0/height) uniform vec2 texIncr; //iterations to do uniform int iterations; //increment between samples uniform float samIncr; //sqrt(max occlusion) uniform float maxOcc; //samples to take const vec2 samples[8] = vec2[8]( vec2(1.0, 0.0), vec2(0.0, 1.0), vec2(-1.0, 0.0), vec2(0.0, -1.0), vec2(1.0, 1.0), vec2(-1.0, 1.0), vec2(1.0, -1.0), vec2(-1.0, -1.0) ); void main() { float zbu = texture2D(tex0, gl_TexCoord[0].xy).x; int hits = 0; for(int i=0; i < 8; i++) { for(int j=0; j < iterations; j++) { float tempDepth = texture2D(tex0, gl_TexCoord[0].xy + (samples[i] * (float(j + 1) * samIncr) * texIncr)).x; if(zbu < tempDepth) { hits += (iterations - j); break; } } } float occlusion = maxOcc * float(hits) / float(iterations * 8); occlusion = 1 - (occlusion * occlusion); gl_FragColor = vec4(occlusion, occlusion, occlusion, 1.0); }";
Al estar realizando las iteraciones, lo que se hace es seguir una linea imaginaria, si el primer punto cumple la condición, los siguientes también la cumplirán, porque detrás de una pared no nos importa que pueda haber, por eso está ahí el break.
Tan sólo quedaría renderizar la escena normalmente y luego aplicar un renderizado multiplicando por la textura que acabamos de crear.
El siguiente paso para conseguir un efecto más realista podría ser considerar las normales de los puntos a la hora de calcular la oclusión, pero vamos a dejar este efecto con la sencillez actual.
No hay comentarios:
Publicar un comentario