Первые проблемы с отображением теней

9

Я впервые реализовал базовое отображение теней в 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, но мне нужно изменить значение в зависимости от расстояния до источника света, чтобы получить более желательные эффекты, что не очень удобно. Я также пытался использовать выборку лицевой стороны при рендеринге в кадровый буфер, что тоже не сильно изменилось.

Другая проблема заключается в том, что пиксели за пределами области видимости источника света затеняются. Единственный объект, который должен отбрасывать тени - это ящик. Думаю, мне следует выбрать более подходящие матрицы проекции и просмотра, но я не уверен, как это сделать. Каковы некоторые общие практики, я должен выбрать ортографическую проекцию?

Если немного погуглить, я понимаю, что эти проблемы не так уж и просты. У кого-нибудь есть какие-нибудь простые в реализации решения этих проблем. Не могли бы вы дать мне несколько дополнительных советов?

Пожалуйста, спросите меня, если вам нужна дополнительная информация о моем коде.

Вот сравнение с и без теневого картирования крупного плана ящика. Самозатенение более заметно.

Grieverheart
источник
Похоже, вы должны описать свою проблему (ы) более подробно. У вас есть одно предложение о ваших проблемах, а следующее предложение предлагает решение для одного. Расскажите нам точно, что это за проблемы, и что вы уже сделали, чтобы попытаться это исправить.
MichaelHouse
Я отредактировал свой пост, надеюсь, теперь лучше.
Grieverheart

Ответы:

6

Вы не должны затенять полигоны, которые обращены назад от света.

Я изменил ваш фрагментный шейдер на код ниже.

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
источник
8

Что касается самозатенения, я не уверен, что вы имеете в виду - я не вижу никаких «теневых угрей» на ящике на скриншоте. Если вы имеете в виду, что лица, обращенные в сторону от света, темные, ну, конечно, это так и должно быть! :) Боковое лицо выглядит немного странно, расколоть диагональ на наполовину освещенную и наполовину затененную. Если вы используете N dot L для освещения и у вас есть хорошие нормали (то есть жесткие нормали в этом кубе, а не сглаженные нормали), этого не должно быть.

Использование смещения многоугольника и / или рисование задних граней на карте теней являются стандартными решениями (ну ... действительно, обходные пути) для угревой тени.

Что касается матрицы проекции, вы должны думать о свете как о камере. Если это точечный источник света, это будет перспективная камера, а если это направленный источник света, то он будет похож на ортографическую камеру. Постройте матрицу проецирования точно так же, как для камеры, используя поле зрения источника света, соотношение сторон и т. Д.

Чтобы ограничить пиксельный шейдер только рисованием пикселей в усечении вида источника света, как только вы вычислите UV карты теней, просто проверьте, находятся ли они между 0 и 1, а discardесли нет (или установите цвет источника света на ноль). Другой способ - установить текстуру карты теней в режим «зажим к границе» и установить нулевой цвет границы, что обеспечит полное затенение всего, что находится вне карты теней.

Натан Рид
источник
Вот крупный план окна и сравнение с и без ссылки на отображение теней . Самозатенение здесь более заметно. Не могли бы вы объяснить, почему вы используете перспективную камеру для точечного освещения и орфографическую для направленного?
Grieverheart
Хорошо, на этом снимке это похоже на теневые прыщи. Рисование задних граней на карте теней (а не передних граней) должно исправить это. Я знаю, что вы сказали, что пытались это сделать, но, возможно, что-то пошло не так? Это просто glCullFace(GL_FRONT). Что касается точки против направления, это все о лучах. Лучи камеры сходятся к точке в перспективной камере, а лучи света сходятся к точке в точечном свете; лучи камеры параллельны для орфографической камеры, а лучи света параллельны для направленного света.
Натан Рид
Я вижу, это имеет смысл. Вот еще одно сравнение с и без выборки лица. У того, кто внизу, включена выборка лица. Я постараюсь использовать ортогональную проекцию для своей матрицы просмотра света, хотя это не должно влиять на проблему самозатенения, верно? Может быть, мой диапазон клипов слишком велик (т.е. у меня от 0,1 до 100)?
Grieverheart
Те выглядят точно так же. Вы используете отбраковку лица для остальной части сцены, верно? Ты сделал glEnable(GL_CULL_FACE)?
Натан Рид
Они не совсем одинаковые, присмотритесь к коробке. На рисунке выше линия тени выпуклая, а линия внизу вогнутая.
Grieverheart
3

Пиксели вне вида источников света темные, поскольку они отбираются вне текстуры глубины источников света.

Когда вы преобразуете вершину с помощью матрицы проекции, вы получите координаты клипа [lightspace_Position] фрагмента. Затем вы конвертируете это в координаты текстуры [UVCoords]. Однако координаты пространства клипа фрагмента могут по-прежнему находиться за пределами диапазона от -1,0 до 1,0 (вне экрана и, следовательно, обрезаться), и, следовательно, преобразованные координаты текстуры могут находиться за пределами диапазона от 0,0 до 1,0 вашей текстуры.

Попробуй это:

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;

Вы также можете умножить матрицу смещения в свой [lightMVP] и избежать преобразования координат в своем фрагментном шейдере.

bgrater
источник
Матрица смещения - это, как обычно, люди. Это намного быстрее, чем ветвление кода в фрагментном шейдере.
Ян Янг