Я впервые реализовал базовое отображение теней в OpenGL с использованием шейдеров, и столкнулся с некоторыми проблемами. Ниже вы можете увидеть пример моей визуализированной сцены:
Процесс отображения теней, которому я следую, состоит в том, что я рендерил сцену в буфер кадров, используя матрицу вида с точки зрения света и матрицы проекции и модели, используемые для нормального рендеринга.
Во втором проходе я отправляю вышеупомянутую матрицу MVP с точки зрения света в вершинный шейдер, который преобразует положение в пространство света. Фрагментный шейдер разделяет перспективу и меняет положение на координаты текстуры.
Вот мой вершинный шейдер,
#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);
}
И мой фрагментный шейдер,
#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);
}
Как вы можете видеть на картинке, одна из проблем - это самозатенение, у ящика есть своя собственная тень. Я попробовал включить смещение полигонов (т.е. glEnable (POLYGON_OFFSET_FILL), glPolygonOffset (GLfloat, GLfloat)), но это не сильно изменилось. Как вы видите в фрагментном шейдере, я установил статическое значение смещения 0,001, но мне нужно изменить значение в зависимости от расстояния до источника света, чтобы получить более желательные эффекты, что не очень удобно. Я также пытался использовать выборку лицевой стороны при рендеринге в кадровый буфер, что тоже не сильно изменилось.
Другая проблема заключается в том, что пиксели за пределами области видимости источника света затеняются. Единственный объект, который должен отбрасывать тени - это ящик. Думаю, мне следует выбрать более подходящие матрицы проекции и просмотра, но я не уверен, как это сделать. Каковы некоторые общие практики, я должен выбрать ортографическую проекцию?
Если немного погуглить, я понимаю, что эти проблемы не так уж и просты. У кого-нибудь есть какие-нибудь простые в реализации решения этих проблем. Не могли бы вы дать мне несколько дополнительных советов?
Пожалуйста, спросите меня, если вам нужна дополнительная информация о моем коде.
Вот сравнение с и без теневого картирования крупного плана ящика. Самозатенение более заметно.