He implementado VSM (y también ESM) en mi motor, pero los resultados no son para mí los que esperaba y vi en muchos ejemplos publicados en la red.
Configuré el filtrado de los mapas de sombras en GL_LINEAR, pero cuando comparo el resultado con el mapa de sombras normal, es visiblemente peor.
Intenté calcular los momentos directamente en el sombreador de luz puntual u obtenerlo de la textura, como está en la mayoría de los tutoriales, pero los resultados son los mismos.
Código:
uniform samplerCubeShadow shadowMap;
...
vec4 worldSpace=inverse(ViewMatrix)*vec4(pos,1);
vec4 coord=LightViewMatrix*worldSpace;
vec4 abs_coord=abs(coord);
float fs_z=-max(abs_coord.x, max(abs_coord.y, abs_coord.z));
vec4 clip=LightProjectionMatrix*vec4(0.0,0.0,fs_z,1.0);
float d2=(clip.z / clip.w)*0.5+0.5; // clamp to [0..1]
...
float shadowTexel=texture(shadowMap,vec4(coord.xyz,d2));
// VSM (Variance Shadow Map)
// get partial derivatives
float dx = dFdx(d2);
float dy = dFdy(d2);
vec2 moments = vec2(d2, d2*d2+0.25*(dx*dx+dy*dy));
return chebychevInequality(moments, shadowTexel);
Usando este código obtengo resultados como en la imagen de arriba. Traté también de no usar samplerCubeShadow pero samplerCube pero los resultados son aún peores. Primero, tengo sombras duras. En segundo lugar, las sombras no llenan el área como deberían cuando obtienen momentos de otra textura. Mira la segunda pantalla. Aquí también se ve el mapa del cubo generado. No es similar a lo que está en el mapa de profundidad, incluso si pongo profundidad / momento1 en los 3 canales.
Shader para conseguir momentos:
// Vartex shader
gl_Position=ModelViewProjectionMatrix*Vertex;
v_position=gl_Position;
// Fragment shader
float depth = v_position.z / v_position.w ;
depth = depth * 0.5 + 0.5; //Don't forget to move away from unit cube ([-1,1]) to [0,1] coordinate system
float moment1 = depth;
float moment2 = depth * depth;
// Adjusting moments (this is sort of bias per pixel) using derivative
float dx = dFdx(depth);
float dy = dFdy(depth);
moment2 += 0.25*(dx*dx+dy*dy) ;
FragColor = vec4( moment1,moment2, 0.0, 0.0 );
Estoy realmente atascado. Espero que me ayudes a resolver mis problemas.
EDITAR:
He encontrado la solución al segundo problema. Había habilitado la mezcla y me dio un mapa de profundidad incorrecto.
También obtengo mejores resultados al primer problema, pero ahora estoy luchando con la profundidad adecuada para compararlo con la profundidad del mapa de sombras.
En SM simple, uso este código:
vec4 worldSpace=inverse(ViewMatrix)*vec4(pos,1);
vec4 coord=LightViewMatrix*worldSpace;
vec4 abs_coord=abs(coord);
float fs_z=-max(abs_coord.x, max(abs_coord.y, abs_coord.z));
vec4 clip=LightProjectionMatrix*vec4(0.0,0.0,fs_z,1.0);
float d=(clip.z / clip.w)*0.5+0.5; // clamp to [0..1]
donde pos es posición en View Space. Luego leo valores del mapa de sombras usando:
texture(shadowMap,vec4(coord.xyz,d))
En VSM almaceno la profundidad en el canal R en textura RG32F. El valor de profundidad se calcula de esta manera:
// VS
gl_Position=ModelViewProjectionMatrix*Vertex;
v_position=gl_Position;
// FS
float depth = v_position.z/v_position.w;
depth = depth * 0.5 + 0.5;
Luego, en el sombreador para punto de luz, uso el vector coord (como en SM estándar) para leer valores del mapa de sombras y esto funciona bien. Pero el problema está en esta parte:
// No shadow if depth of fragment is in front
if ( moments.x <= distance)
return 1.0;
¿En qué coordenadas debe llegar la distancia? ¿En qué coordenadas tengo profundidad del mapa de sombras? Debería ser lineal? ¿Alguien podría explicarme eso? Estoy un poco confundido en este momento, intenté muchas formas de obtener esto y todos los resultados no son como esperaba.
EDIT2: Siguiendo el consejo de JarkkoL y este tutorial, cambié mi código. Ahora estoy almacenando profundidad usando este código:
// VS
v_position=ModelViewMatrix*Vertex;
gl_Position=ProjectionMatrix*v_position;
// FS
const float Near = 0.1;
const float Far = 90.0; // camera far plane
const float LinearDepthConstant = 1.0 / (Far - Near);
float depth = length(v_position)*LinearDepthConstant;
Y lo comparo con el valor que obtengo de esta manera:
float dist=length( vec3(inverse(ViewMatrix)*vec4(pos,1.0)) - PointLight.position )*LinearDepthConstant; // pos is read from depth buffer and is in view space so I want invert it to world space as it was in tutorial
Y aqui esta el resultado:
En el círculo rojo marqué los bordes visibles entre las caras del mapa del cubo. Todavía hay algo mal. Creo que podría ser algo relacionado con la inversión de View Matrix, pero no estoy seguro.