Eu implementei o VSM (e também o ESM) no meu mecanismo, mas os resultados não são para mim como eu esperava e vi em muitos exemplos publicados na rede.
Defino a filtragem dos mapas de sombra como GL_LINEAR, mas quando comparo o resultado ao mapa de sombras normal, é visivelmente pior.
Tentei calcular momentos diretamente no shader de luz pontual ou obtê-lo da textura, como é na maioria dos tutoriais, mas os resultados são os mesmos.
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 esse código, obtenho resultados como na imagem acima. Tentei também não usar samplerCubeShadow, mas samplerCube, mas os resultados são ainda piores. Primeiro, tenho sombras duras. Segundo, as sombras não preenchem a área como deveriam ao obter momentos de outra textura. Olhe para a segunda tela. Aqui também está o mapa do cubo gerado. Não é semelhante ao que está no mapa de profundidade, mesmo se eu colocar profundidade / momento1 em todos os 3 canais.
Shader para obter 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 );
Eu estou realmente preso. Espero que você ajude mi a resolver meus problemas.
EDITAR:
Eu encontrei a solução para o segundo problema. Eu havia habilitado a mistura e isso me deu um mapa de profundidade errado.
Também obtenho um melhor resultado para o primeiro problema, mas agora estou lutando com profundidade adequada para compará-lo com a profundidade do mapa de sombras.
No SM simples, eu 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]
onde pos é posição no espaço de exibição. Então eu li valores do mapa de sombra usando:
texture(shadowMap,vec4(coord.xyz,d))
No VSM, guardo profundidade no canal R na textura RG32F. O valor da profundidade é calculado desta maneira:
// VS
gl_Position=ModelViewProjectionMatrix*Vertex;
v_position=gl_Position;
// FS
float depth = v_position.z/v_position.w;
depth = depth * 0.5 + 0.5;
Então, no shader para o ponto de luz, eu uso o vetor coord (como no padrão SM) para ler os valores do mapa de sombras e isso funciona bem. Mas o problema está nesta parte:
// No shadow if depth of fragment is in front
if ( moments.x <= distance)
return 1.0;
Em que coordenadas a distância deve ser obtida? Em que coordenadas tenho profundidade do mapa de sombras? Deve ser linear? Alguém poderia me explicar isso? Estou um pouco confuso agora, tentei várias maneiras de conseguir isso e todos os resultados não são como eu esperava.
EDIT2: Seguindo a dica do JarkkoL e este tutorial, mudei meu código. Agora estou armazenando profundidade 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;
E eu comparo com o valor que obtenho desta maneira:
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
E aqui está o resultado:
No círculo vermelho, marquei as bordas visíveis entre as faces do mapa do cubo. Ainda há algo errado. Eu acho que poderia ser algo com a inversão da View Matrix, mas não tenho certeza.