Почему размытие карты глубины VSM приводит к странным результатам?

#opengl #shadow-mapping

#opengl #отображение теней

Вопрос:

Я пытаюсь реализовать отображение дисперсионных теней для направленных теней в моем движке рендеринга с помощью OpenGL.

Я прочитал несколько статей, таких как — https://developer.nvidia.com/gpugems/gpugems3/part-ii-light-and-shadows/chapter-8-summed-area-variance-shadow-maps , https://graphics.stanford.edu /~mdfisher/Shadows.html чтобы развить это.

Основной поток алгоритма заключается в следующем:

  1. Сохраните глубину и глубину ^ 2 в текстуре глубины.
  2. Примените двухпроходное размытие по Гауссу с ядром 5 x 5 и 10 проходами.
  3. Выберите значение глубины, вычислите расстояние фрагмента от источника света и
  4. Поместите их в неравенство Чебышева, чтобы определить максимальную вероятность нахождения фрагмента в тени
  5. Используйте результат, чтобы сделать фрагмент темным.

Вот мой шейдер глубины для направленного света с матрицей ортографической проекции:

 #version 440 core

uniform float farPlane;
uniform vec3 lightPos;
uniform mat4 directional_light_space_matrix;

in vec4 FragPos;
out vec2 depth;

void main()
{   

     vec4 FragPosLightSpace = directional_light_space_matrix * FragPos;
     float d = FragPosLightSpace.z / FragPosLightSpace.w;

     d = d * 0.5   0.5;

     float m1 = d;
     float m2 = d * d;

     float dx = dFdx(depth.x);
     float dy = dFdx(depth.y);

     m2  = 0.25 * (dx * dx   dy * dy);

     depth.r = m1;
     depth.g = m2;

}
 

Вот фрагмент фрагментного шейдера, который проверяет, насколько освещен фрагмент.

 float linstep(float mi, float ma, float v)
{
    return clamp ((v - mi)/(ma - mi), 0, 1);
}

float ReduceLightBleeding(float p_max, float Amount) 
{
     return linstep(Amount, 1, p_max); 
} 

float chebyshevUpperBound(float dist, vec2 moments)
{
    float p_max;
    if(dist <= moments.x)
    {
         return 1.0;
    }

    float variance = moments.y - (moments.x * moments.x);
    variance = max(variance, 0.1);
    float d = moments.x - dist;

    p_max = variance / (variance   d * d);

    return ReduceLightBleeding(p_max, 1.0);
}

float CheckDirectionalShadow(float bias, vec3 lightpos, vec3 FragPos)
{       
     vec3 projCoords = FragPosLightSpace.xyz / FragPosLightSpace.w;
     projCoords = projCoords * 0.5   0.5;

     vec2 closest_depth = texture(shadow_depth_map_directional, projCoords.xy).rg;

     return chebyshevUpperBound(projCoords.z, closest_depth);
}
 

Here’s the Two Pass Gaussian Blur shader.

 #version 440 core

layout (location = 0) out vec2 out_1;

in vec2 TexCoords;

uniform sampler2D inputTexture_1;

uniform bool horizontal;
float weights[5] = float[](0.227027, 0.1945946, 0.1216216, 0.054054, 0.016216);

void main()
{
    vec2 tex_offset = 1.0 / textureSize(inputTexture_1,0);
    vec2 o1 = texture(inputTexture_1, TexCoords).rg * weights[0];

    if(horizontal)
    {
        for(int i=1; i<4; i  )
        {
            o1  = texture(inputTexture_1, TexCoords   vec2(tex_offset.x * i, 0.0)).rg * weights[i];
            o1  = texture(inputTexture_1, TexCoords - vec2(tex_offset.x * i, 0.0)).rg * weights[i];
        }
    }
    else
    {
        for(int i=1; i<4; i  )
        {
            o1  = texture(inputTexture_1, TexCoords   vec2(0.0, tex_offset.y * i)).rg * weights[i];
            o1  = texture(inputTexture_1, TexCoords - vec2(0.0, tex_offset.y * i)).rg * weights[i];
        }
    }

    out_1 = o1;
}
 

Я добавляю свой код генерации фреймбуфера для получения информации о том, как я сохраняю моменты.

 // directional ----------------------------------------------------------------------------------------------------------------------------------------------
glGenFramebuffers(1, amp;directional_shadow_framebuffer);

glGenTextures(1, amp;directional_shadow_framebuffer_depth_texture);
glBindTexture(GL_TEXTURE_2D, directional_shadow_framebuffer_depth_texture); 

glTexImage2D(GL_TEXTURE_2D, 0, GL_RG32F, shadow_map_width, shadow_map_height, 0, GL_RG, GL_FLOAT, NULL);

float border_color[] = { 0.0f,0.0f,0.0f,1.0f };

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border_color);

glBindFramebuffer(GL_FRAMEBUFFER, directional_shadow_framebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, directional_shadow_framebuffer_depth_texture, 0);

glGenRenderbuffers(1, amp;directional_shadow_framebuffer_renderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, directional_shadow_framebuffer_renderbuffer);

glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, shadow_map_width, shadow_map_height);

glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, directional_shadow_framebuffer_renderbuffer);


if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
    LOGGER->log(ERROR, "Renderer : createShadowMapBuffer", "Directional Shadow Framebuffer is incomplete!");

glBindRenderbuffer(GL_RENDERBUFFER, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// ----------------------------------------------------------------------------------------------------------------------------------------------
 

Результаты вышеуказанных операций далеки от ожиданий. Вместо мягких теней полутени я получаю резкие тени, похожие на капли.

Тени

Вот как выглядит первый момент (глубина), а второй момент практически такой же, но темнее.

введите описание изображения здесь

Я пробовал экспериментировать с минимальной дисперсией, размером ядра тени, гауссовскими выборками, проходами размытия.. но я не приблизился к решению.

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

Мои последние вопросы :

  1. Является ли моя реализация VSM неверной?
  2. Почему я не вижу мягких полутеней?
  3. У меня не очень хорошее представление о том, как фильтруется моя текстура, что-то не так в коде генерации фреймбуфера?

Ответ №1:

Итак, я решил проблему.

Реализация в полном порядке, но минимальная дисперсия и параметр количества уменьшения светового потока требуют настройки.

Я обнаружил, что уменьшение параметра минимальной дисперсии еще больше смягчит тени, но значительно увеличит выделение света. Чтобы противостоять этому побочному эффекту, мы можем настроить значение p_max на значение 0, когда оно ниже определенного порога, в противном случае изменить масштаб между 0 и 1. Это именно то, что делает функция ReduceLightBleeding, которая также описана на том же сайте, ссылка на который приведена выше. Но увеличение параметра amount в ReduceLightBleeding приведет к тому, что тени будут выглядеть как капли, что видно на скриншотах, которые я опубликовал выше.

Мне удалось настроить минимальную дисперсию и уменьшить количество незначительных кровотечений, чтобы найти оптимальное место. Однако я никогда не мог полностью избавиться от этого артефакта.

Лучшей альтернативой отображению теней дисперсии является его расширение — карты теней экспоненциальной дисперсии.

Я не совсем понимаю математику, но мне все же удалось довольно легко ее реализовать. Проверьте этот вопрос на gamedev.stackexchange для подсказок — EVSM.

ESVM проделал отличную работу, уменьшив кровотечение до такой степени, что его можно либо не заметить, либо просто проигнорировать.