J'ai implémenté le mappage d'ombres de base pour la première fois dans OpenGL à l'aide de shaders et je rencontre des problèmes. Ci-dessous, vous pouvez voir un exemple de ma scène rendue:
Le processus de mappage des ombres que je suis consiste à rendre la scène au tampon d'images en utilisant une matrice de vue du point de vue de la lumière et les matrices de projection et de modèle utilisées pour le rendu normal.
Dans la deuxième passe, j'envoie la matrice MVP ci-dessus du point de vue de la lumière au vertex shader qui transforme la position en espace lumineux. Le fragment shader divise la perspective et change la position en coordonnées de texture.
Voici mon vertex shader,
#version 150 core
uniform mat4 ModelViewMatrix;
uniform mat3 NormalMatrix;
uniform mat4 MVPMatrix;
uniform mat4 lightMVP;
uniform float scale;
in vec3 in_Position;
in vec3 in_Normal;
in vec2 in_TexCoord;
smooth out vec3 pass_Normal;
smooth out vec3 pass_Position;
smooth out vec2 TexCoord;
smooth out vec4 lightspace_Position;
void main(void){
pass_Normal = NormalMatrix * in_Normal;
pass_Position = (ModelViewMatrix * vec4(scale * in_Position, 1.0)).xyz;
lightspace_Position = lightMVP * vec4(scale * in_Position, 1.0);
TexCoord = in_TexCoord;
gl_Position = MVPMatrix * vec4(scale * in_Position, 1.0);
}
Et mon fragment shader,
#version 150 core
struct Light{
vec3 direction;
};
uniform Light light;
uniform sampler2D inSampler;
uniform sampler2D inShadowMap;
smooth in vec3 pass_Normal;
smooth in vec3 pass_Position;
smooth in vec2 TexCoord;
smooth in vec4 lightspace_Position;
out vec4 out_Color;
float CalcShadowFactor(vec4 lightspace_Position){
vec3 ProjectionCoords = lightspace_Position.xyz / lightspace_Position.w;
vec2 UVCoords;
UVCoords.x = 0.5 * ProjectionCoords.x + 0.5;
UVCoords.y = 0.5 * ProjectionCoords.y + 0.5;
float Depth = texture(inShadowMap, UVCoords).x;
if(Depth < (ProjectionCoords.z + 0.001)) return 0.5;
else return 1.0;
}
void main(void){
vec3 Normal = normalize(pass_Normal);
vec3 light_Direction = -normalize(light.direction);
vec3 camera_Direction = normalize(-pass_Position);
vec3 half_vector = normalize(camera_Direction + light_Direction);
float diffuse = max(0.2, dot(Normal, light_Direction));
vec3 temp_Color = diffuse * vec3(1.0);
float specular = max( 0.0, dot( Normal, half_vector) );
float shadowFactor = CalcShadowFactor(lightspace_Position);
if(diffuse != 0 && shadowFactor > 0.5){
float fspecular = pow(specular, 128.0);
temp_Color += fspecular;
}
out_Color = vec4(shadowFactor * texture(inSampler, TexCoord).xyz * temp_Color, 1.0);
}
L'un des problèmes est l'ombrage automatique comme vous pouvez le voir sur la photo, la caisse a sa propre ombre projetée sur elle-même. Ce que j'ai essayé, c'est d'activer le décalage de polygone (c'est-à-dire glEnable (POLYGON_OFFSET_FILL), glPolygonOffset (GLfloat, GLfloat)) mais cela n'a pas beaucoup changé. Comme vous le voyez dans le fragment shader, j'ai mis une valeur de décalage statique de 0,001 mais je dois changer la valeur en fonction de la distance de la lumière pour obtenir des effets plus souhaitables, ce qui n'est pas très pratique. J'ai également essayé d'utiliser l'abattage de face avant lors du rendu sur le framebuffer, cela n'a pas trop changé.
L'autre problème est que les pixels en dehors du tronc de vue de la lumière sont ombrés. Le seul objet censé pouvoir projeter des ombres est la caisse. Je suppose que je devrais choisir des matrices de projection et de vue plus appropriées, mais je ne sais pas comment faire cela. Quelles sont les pratiques courantes, dois-je choisir une projection orthographique?
De googler un peu, je comprends que ces questions ne sont pas si triviales. Quelqu'un at-il des solutions faciles à implémenter à ces problèmes? Pourriez-vous me donner quelques conseils supplémentaires?
Veuillez me demander si vous avez besoin de plus d'informations sur mon code.
Voici une comparaison avec et sans cartographie d'ombre d'un gros plan de la caisse. L'auto-observation est plus visible.
la source
Réponses:
Vous ne devez pas masquer les polygones à l'arrière de la lumière.
J'ai changé votre shader de fragment en code ci-dessous.
la source
En ce qui concerne l'auto-observation, je ne sais pas exactement à quoi vous faites référence - je ne vois pas "d'acné fictive" sur la caisse dans votre capture d'écran. Si vous voulez dire que les visages opposés à la lumière sont sombres, eh bien, bien sûr qu'ils le sont - ils devraient l'être! :) La face latérale a l'air un peu bizarre, divisée en diagonale à moitié éclairée et à moitié ombragée. Si vous utilisez N point L pour l'éclairage et que vous avez de bonnes normales (c'est-à-dire des normales dures sur ce cube, pas des normales lissées) cela ne devrait pas se produire.
L'utilisation du décalage de polygone et / ou le dessin des faces arrière dans la carte d'ombre sont les solutions standard (enfin ... des solutions de contournement vraiment) pour l'acné des ombres.
Quant à la matrice de projection, vous devez considérer la lumière comme une caméra. Si c'est une lumière ponctuelle, ce serait une caméra en perspective et si c'est une lumière directionnelle, ce serait comme une caméra orthographique. Construisez la matrice de projection exactement comme vous le feriez pour une caméra, en utilisant le champ de vision de la lumière, les proportions, etc.
Pour restreindre le pixel shader pour ne dessiner que les pixels dans le tronc de vue de la lumière, une fois que vous avez calculé les UV de la carte d'ombre, vérifiez simplement s'ils sont compris entre 0 et 1 (
discard
sinon définissez la couleur de la lumière sur zéro). Une autre façon consiste à définir la texture de la texture de l'ombre sur le mode "restreindre à la bordure" et à définir une couleur de bordure de zéro, ce qui garantira que tout ce qui se trouve en dehors de la texture de l'ombre est entièrement masqué.la source
glCullFace(GL_FRONT)
. Quant au point vs directionnel, il s'agit de rayons. Les rayons de la caméra convergent vers un point dans une caméra en perspective et les rayons lumineux convergent vers un point dans une lumière ponctuelle; les rayons des caméras sont parallèles pour une caméra orthographique et les rayons lumineux sont parallèles pour une lumière directionnelle.glEnable(GL_CULL_FACE)
?Les pixels à l'extérieur de la vue des lumières sont sombres car ils sont échantillonnés de l'extérieur de la texture de profondeur des lumières.
Lorsque vous transformez le sommet par la matrice de projection, vous obtenez les coordonnées du clip [lightspace_Position] du fragment. Vous convertissez ensuite cela en coordonnées de texture [UVCoords]. Cependant, les coordonnées d'espace de clip d'un fragment peuvent toujours être en dehors de la plage de -1,0 à 1,0 (hors de l'écran et donc écrêtées) et donc les coordonnées de texture converties peuvent être en dehors de la plage de 0,0 à 1,0 de votre texture.
Essaye ça:
Vous pouvez également multiplier une matrice de biais dans votre [lightMVP] et éviter de faire la conversion de coordonnées dans votre fragment shader.
la source