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:
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.
fuente
Respuestas:
No debe sombrear los polígonos que están enfrentados desde la luz.
Cambié tu sombreador de fragmentos al siguiente código.
fuente
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
discard
lo 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.fuente
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.glEnable(GL_CULL_FACE)
?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:
También puede multiplicar una matriz de sesgo en su [lightMVP] y evitar hacer la conversión de coordenadas en su sombreador de fragmentos.
fuente