J'ai implémenté VSM (et aussi ESM) dans mon moteur mais les résultats ne sont pas pour moi comme je m'y attendais et vu dans de nombreux exemples publiés sur le réseau.
J'ai défini le filtrage des mappages d'ombres sur GL_LINEAR mais lorsque je compare le résultat à un mappage d'ombres normal, c'est visiblement pire.
J'ai essayé de calculer des moments directement dans le nuanceur de points ou de les obtenir à partir de la texture comme dans la plupart des didacticiels, mais les résultats sont les mêmes.
Code:
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);
En utilisant ce code, j'obtiens des résultats comme sur l'image ci-dessus. J'ai aussi essayé de ne pas utiliser samplerCubeShadow mais samplerCube mais les résultats sont encore pires. Tout d'abord, j'ai eu des ombres dures. Deuxièmement, les ombres ne remplissent pas la zone comme elles le devraient lorsque vous obtenez des moments d'une autre texture. Regardez le deuxième écran. Voici également la carte des cubes générée. Ce n'est pas similaire à ce qui est dans la carte en profondeur même si je mets depth / moment1 dans les 3 canaux.
Shader pour obtenir des moments:
// 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 );
Je suis vraiment coincé. J'espère que vous aiderez mi à résoudre mes problèmes.
ÉDITER:
J'ai trouvé la solution au deuxième problème. J'avais activé le mélange et cela m'a donné une mauvaise carte de profondeur.
J'obtiens également de meilleurs résultats au premier problème, mais maintenant je me bats avec la bonne profondeur pour la comparer avec la profondeur de la carte d'ombre.
En simple SM j'utilise ce code:
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]
où pos est la position dans l'espace de vue. Ensuite, j'ai lu les valeurs de la carte fantôme en utilisant:
texture(shadowMap,vec4(coord.xyz,d))
Dans VSM, je stocke la profondeur dans le canal R dans la texture RG32F. La valeur de profondeur est calculée de cette manière:
// VS
gl_Position=ModelViewProjectionMatrix*Vertex;
v_position=gl_Position;
// FS
float depth = v_position.z/v_position.w;
depth = depth * 0.5 + 0.5;
Ensuite, dans le shader pour la lumière ponctuelle, j'utilise le vecteur coord (comme dans le SM standard) pour lire les valeurs de la carte des ombres et cela fonctionne bien. Mais le problème est dans cette partie:
// No shadow if depth of fragment is in front
if ( moments.x <= distance)
return 1.0;
Dans quelles coordonnées la distance doit-elle être obtenue? Dans quelles coordonnées j'ai la profondeur de la carte d'ombre? Il devrait être linéaire? Quelqu'un pourrait-il m'expliquer cela? Je suis un peu confus en ce moment, j'ai essayé de nombreuses façons d'obtenir cela et tous les résultats ne sont pas comme je m'y attendais.
EDIT2: Suite à l'astuce JarkkoL et à ce tutoriel, j'ai changé mon code. Maintenant, je stocke la profondeur en utilisant ce code:
// 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;
Et je le compare avec la valeur que j'obtiens de cette façon:
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
Et voici le résultat:
Dans le cercle rouge, j'ai marqué des bordures visibles entre les faces de la carte du cube. Il y a encore quelque chose qui ne va pas. Je pense que cela pourrait être quelque chose d'inverser View Matrix mais je ne suis pas sûr.
Réponses:
En ce qui concerne les coutures cubemap, vous pouvez simplement filtrer sur les bords.
Voir:
glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
En ce qui concerne votre qualité réelle d'ombre, tous les avantages des techniques VSM et ESM proviennent de la forme spéciale que prend le test de visibilité. Vous voudrez probablement introduire un facteur constant pour sur ou sous-assombrir les ombres, afin que le bord ne soit pas si dur.
Pour les données ESM, c'est simple:
la source