Problèmes de mappage d'ombre pour la première fois

9

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:

entrez la description de l'image ici

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.

Grieverheart
la source
Il semble que vous devriez décrire vos problèmes plus en détail. Vous avez une phrase sur vos problèmes et la phrase suivante suggère une solution. Dites-nous exactement quels sont les problèmes et ce que vous avez déjà fait pour essayer de le résoudre.
MichaelHouse
J'ai édité mon article, j'espère que c'est mieux maintenant.
Grieverheart

Réponses:

6

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.

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 fndotl = dot(Normal, light_Direction);
    float shadowFactor = CalcShadowFactor(lightspace_Position);
    float diffuse = max(0.0, fndotl) * shadowFactor + 0.2;
    vec3 temp_Color = vec3(diffuse);

    float specular = max( 0.0, dot( Normal, half_vector) );

    if(shadowFactor > 0.9){
        float fspecular = pow(specular, 128.0);
        temp_Color += fspecular;
    }

    out_Color = vec4(shadowFactor * texture(inSampler, TexCoord).xyz * temp_Color, 1.0);
}
kochol
la source
8

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 ( discardsinon 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é.

Nathan Reed
la source
Voici un gros plan de la boîte et une comparaison avec et sans lien de mappage d'ombre . L'auto-observation est plus visible ici. Pourriez-vous expliquer pourquoi vous utiliseriez une caméra en perspective pour les lumières ponctuelles et une caméra orthographique pour la direction?
Grieverheart
D'accord, dans cette photo, cela ressemble à de l'acné fictive. Le dessin des faces arrière dans la carte d'ombre (et non des faces avant) devrait résoudre ce problème. Je sais que vous avez dit que vous avez essayé cela, mais peut-être que quelque chose s'est mal passé? C'est juste 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.
Nathan Reed
Je vois, cela a du sens. Voici une autre comparaison avec et sans abattage facial. Dans celui du bas, l'élimination des visages est activée. Je vais essayer d'utiliser la projection orthographique pour ma matrice de vision de la lumière, cela ne devrait pas affecter le problème de l'ombrage automatique, non? Peut-être que ma plage de clips est trop grande (c'est-à-dire que j'ai 0,1 à 100)?
Grieverheart
Celles-ci se ressemblent exactement. Vous utilisez l'abattage en face arrière pour le reste de la scène, non? L'avez-vous fait glEnable(GL_CULL_FACE)?
Nathan Reed
Ils ne sont pas exactement les mêmes, regardez attentivement la boîte. Dans l'image ci-dessus, la ligne d'ombre est convexe tandis que celle du bas est concave.
Grieverheart
3

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:

if(UVCoords.x >= 0.0 && UVCoords.x <= 1.0 && UVCoords.y >= 0.0 && UVCoords.y <= 1.0 && (Depth < (ProjectionCoords.z + 0.001)))
    return 0.5;

Vous pouvez également multiplier une matrice de biais dans votre [lightMVP] et éviter de faire la conversion de coordonnées dans votre fragment shader.

bgrater
la source
La matrice de biais est généralement la façon dont les gens vont. C'est beaucoup plus rapide que le branchement de code dans un fragment shader.
Ian Young