Problemas de mapeo de sombras por primera vez

9

He implementado el mapeo básico de sombras por primera vez en OpenGL usando sombreadores y estoy enfrentando algunos problemas. A continuación puede ver un ejemplo de mi escena renderizada:

ingrese la descripción de la imagen aquí

El proceso del mapeo de sombras que estoy siguiendo es que renderizo la escena al framebuffer usando una matriz de vista desde el punto de vista de la luz y las matrices de proyección y modelo utilizadas para el renderizado normal.

En la segunda pasada, envío la matriz MVP anterior desde el punto de vista de la luz al sombreador de vértices que transforma la posición en espacio de luz. El sombreador de fragmentos divide la perspectiva y cambia la posición a coordenadas de textura.

Aquí está mi sombreador de vértices,


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

Y mi sombreador de fragmentos,


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

Uno de los problemas es el auto sombreado, como puede ver en la imagen, la caja tiene su propia sombra proyectada sobre sí misma. Lo que he intentado es habilitar el desplazamiento del polígono (es decir, glEnable (POLYGON_OFFSET_FILL), glPolygonOffset (GLfloat, GLfloat)) pero no cambió mucho. Como puede ver en el sombreador de fragmentos, puse un valor de desplazamiento estático de 0.001 pero tengo que cambiar el valor dependiendo de la distancia de la luz para obtener efectos más deseables, lo que no es muy útil. También intenté usar el sacrificio de la cara frontal cuando renderizo al framebuffer, eso tampoco cambió mucho.

El otro problema es que los píxeles fuera del tronco de la vista de la Luz se sombrean. El único objeto que se supone que puede proyectar sombras es la caja. Supongo que debería elegir una proyección más apropiada y ver matrices, pero no estoy seguro de cómo hacerlo. ¿Cuáles son algunas prácticas comunes, debo elegir una proyección ortográfica?

Al buscar en Google un poco, entiendo que estos problemas no son tan triviales. ¿Alguien tiene alguna solución fácil de implementar para estos problemas? ¿Me podría dar algunos consejos adicionales?

Pregúnteme si necesita más información sobre mi código.

Aquí hay una comparación con y sin mapeo de sombras de un primer plano de la caja. El auto-sombreado es más visible.

Corazón triste
fuente
Parece que deberías describir tus problemas con más detalle. Tiene una oración sobre sus problemas y la siguiente oración sugiere una solución para una. Cuéntanos exactamente cuáles son los problemas y lo que ya has hecho para intentar solucionarlo.
MichaelHouse
Edité mi publicación, espero que sea mejor ahora.
Grieverheart 01 de

Respuestas:

6

No debe sombrear los polígonos que están enfrentados desde la luz.

Cambié tu sombreador de fragmentos al siguiente código.

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
fuente
8

Con respecto al auto sombreado, no estoy seguro de a qué te refieres: no veo ningún "acné en la sombra" en la caja en tu captura de pantalla. Si quiere decir que los rostros que se alejan de la luz son oscuros, bueno, por supuesto que lo son, ¡deberían serlo! :) La cara lateral se ve un poco rara, divide la diagonal en mitad iluminada y mitad sombreada. Si está utilizando N dot L para la iluminación, y tiene buenas normales (es decir, normales normales en este cubo, no normales suavizadas), eso no debería suceder.

Usar el desplazamiento del polígono y / o dibujar las caras posteriores en el mapa de sombras son las soluciones estándar (bueno ... soluciones alternativas realmente) para el acné en las sombras.

En cuanto a la matriz de proyección, debe pensar en la luz como una cámara. Si es una luz puntual, sería una cámara de perspectiva y si es una luz direccional sería como una cámara ortográfica. Construya la matriz de proyección de la misma manera que lo haría para una cámara, utilizando el campo de visión de la luz, la relación de aspecto, etc.

Para restringir el sombreador de píxeles para que solo dibuje los píxeles en el centro de la vista de la luz, una vez que calcule los rayos UV del mapa de sombras, simplemente verifique si están entre 0 y 1 y, de discardlo contrario, (o configure el color de la luz en cero). Otra forma es establecer la textura del mapa de sombras en el modo de "sujeción al borde" y establecer un color de borde de cero, lo que asegurará que todo lo que esté fuera del mapa de sombras se sombree completamente.

Nathan Reed
fuente
Aquí hay un primer plano del cuadro y una comparación con y sin enlace de mapeo de sombras . El auto-sombreado es más visible aquí. ¿Podría explicar por qué usaría una cámara de perspectiva para luces puntuales y ortográfica para direccional?
Grieverheart 01 de
Bien, en esa toma parece un acné en la sombra. Dibujar caras hacia atrás en el mapa de sombras (y no caras frontales) debería solucionarlo. Sé que dijiste que lo intentaste, pero ¿tal vez algo salió mal? Es sólo glCullFace(GL_FRONT). En cuanto a punto vs direccional, se trata de los rayos. Los rayos de cámara convergen a un punto en una cámara de perspectiva y los rayos de luz convergen a un punto en un punto de luz; los rayos de la cámara son paralelos para una cámara ortográfica y los rayos de luz son paralelos para una luz direccional.
Nathan Reed
Ya veo, eso tiene sentido. Aquí hay otra comparación con y sin sacrificio facial. El que está en la parte inferior tiene habilitada la selección de caras. Trataré de usar la proyección ortográfica para mi matriz Light View, eso no debería afectar el problema del auto sombreado, ¿verdad? ¿Quizás mi rango de clips es demasiado grande (es decir, tengo de 0.1 a 100)?
Grieverheart 01 de
Esos se ven exactamente iguales. Estás usando la selección de la cara posterior para el resto de la escena, ¿verdad? Hizo usted glEnable(GL_CULL_FACE)?
Nathan Reed
No son exactamente lo mismo, mira de cerca la caja. En la imagen de arriba, la línea de sombra es convexa mientras que la de abajo es cóncava.
Grieverheart 01 de
3

Los píxeles fuera de la vista de luces son oscuros porque se están muestreando desde fuera de la textura de profundidad de luces.

Cuando transforma el vértice por la matriz de proyección, obtiene las coordenadas del clip [lightspace_Position] del fragmento. Luego está convirtiendo eso en coordenadas de textura [UVCoords]. Sin embargo, las coordenadas de espacio de recorte de un fragmento aún pueden estar fuera del rango de -1.0 a 1.0 (fuera de la pantalla y, por lo tanto, recortado) y, por lo tanto, las coordenadas de textura convertidas pueden estar fuera del rango de 0.0 a 1.0 de su textura.

Prueba esto:

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;

También puede multiplicar una matriz de sesgo en su [lightMVP] y evitar hacer la conversión de coordenadas en su sombreador de fragmentos.

bgrater
fuente
La matriz de sesgo suele ser la forma en que las personas van. Es mucho más rápido que ramificar código en un fragment shader.
Ian Young