Я впервые реализовал базовое отображение теней в 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, но мне нужно изменить значение в зависимости от расстояния до источника света, чтобы получить более желательные эффекты, что не очень удобно. Я также пытался использовать выборку лицевой стороны при рендеринге в кадровый буфер, что тоже не сильно изменилось.
Другая проблема заключается в том, что пиксели за пределами области видимости источника света затеняются. Единственный объект, который должен отбрасывать тени - это ящик. Думаю, мне следует выбрать более подходящие матрицы проекции и просмотра, но я не уверен, как это сделать. Каковы некоторые общие практики, я должен выбрать ортографическую проекцию?
Если немного погуглить, я понимаю, что эти проблемы не так уж и просты. У кого-нибудь есть какие-нибудь простые в реализации решения этих проблем. Не могли бы вы дать мне несколько дополнительных советов?
Пожалуйста, спросите меня, если вам нужна дополнительная информация о моем коде.
Вот сравнение с и без теневого картирования крупного плана ящика. Самозатенение более заметно.
источник
Ответы:
Вы не должны затенять полигоны, которые обращены назад от света.
Я изменил ваш фрагментный шейдер на код ниже.
источник
Что касается самозатенения, я не уверен, что вы имеете в виду - я не вижу никаких «теневых угрей» на ящике на скриншоте. Если вы имеете в виду, что лица, обращенные в сторону от света, темные, ну, конечно, это так и должно быть! :) Боковое лицо выглядит немного странно, расколоть диагональ на наполовину освещенную и наполовину затененную. Если вы используете N dot L для освещения и у вас есть хорошие нормали (то есть жесткие нормали в этом кубе, а не сглаженные нормали), этого не должно быть.
Использование смещения многоугольника и / или рисование задних граней на карте теней являются стандартными решениями (ну ... действительно, обходные пути) для угревой тени.
Что касается матрицы проекции, вы должны думать о свете как о камере. Если это точечный источник света, это будет перспективная камера, а если это направленный источник света, то он будет похож на ортографическую камеру. Постройте матрицу проецирования точно так же, как для камеры, используя поле зрения источника света, соотношение сторон и т. Д.
Чтобы ограничить пиксельный шейдер только рисованием пикселей в усечении вида источника света, как только вы вычислите UV карты теней, просто проверьте, находятся ли они между 0 и 1, а
discard
если нет (или установите цвет источника света на ноль). Другой способ - установить текстуру карты теней в режим «зажим к границе» и установить нулевой цвет границы, что обеспечит полное затенение всего, что находится вне карты теней.источник
glCullFace(GL_FRONT)
. Что касается точки против направления, это все о лучах. Лучи камеры сходятся к точке в перспективной камере, а лучи света сходятся к точке в точечном свете; лучи камеры параллельны для орфографической камеры, а лучи света параллельны для направленного света.glEnable(GL_CULL_FACE)
?Пиксели вне вида источников света темные, поскольку они отбираются вне текстуры глубины источников света.
Когда вы преобразуете вершину с помощью матрицы проекции, вы получите координаты клипа [lightspace_Position] фрагмента. Затем вы конвертируете это в координаты текстуры [UVCoords]. Однако координаты пространства клипа фрагмента могут по-прежнему находиться за пределами диапазона от -1,0 до 1,0 (вне экрана и, следовательно, обрезаться), и, следовательно, преобразованные координаты текстуры могут находиться за пределами диапазона от 0,0 до 1,0 вашей текстуры.
Попробуй это:
Вы также можете умножить матрицу смещения в свой [lightMVP] и избежать преобразования координат в своем фрагментном шейдере.
источник