Os mapas de sombra de variação não desejam renderizar corretamente

8

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.

Tela

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 );

Tela

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:

Screen3

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.

atormentar
fonte
Adicionei informações sobre meu progresso atual.
Harry
11
Você não deve usar a profundidade não linear que você está usando no momento.
JarkkoL
Obrigado pela dica. Tentei segui-lo e editei a postagem com meus novos resultados.
Harry
Tente definir o modo de endereço a braçadeira para o Cubemap
JarkkoL
Isso realmente ajuda. Eu configurei GL_REPEAT para cubemaps e esqueci completamente disso. Enfim, as sombras vsm ainda não são renderizadas como um simples mapa de sombras.
Harry

Respostas:

1

Em relação às costuras do cubemap, é possível filtrar apenas as arestas.

Vejo: glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);

Quanto à sua qualidade real de sombra, todo o benefício das técnicas VSM e ESM vem da forma especial que o teste de visibilidade assume. Você provavelmente deseja introduzir um fator constante para escurecer demais ou abaixo as sombras, para que a borda não seja tão difícil.

Para dados do ESM, é simples:

light = exp(factor * (occluder - receiver));
GameGraph
fonte