Путаница с отображением теней и значениями глубины

Я делаю отображение теней для прожекторов в отложенном рендерере OpenGL 4.3.

Я пытался следовать некоторым учебным пособиям по этому вопросу и смоделировал фрагментный шейдер после него, но чего я не понимаю, так это окончательного сравнения для расчета коэффициента тени. Значения, которые я выбираю из карты глубины («unifShadowTexture»), находятся в диапазоне [0, 1], поскольку они взяты непосредственно из буфера глубины, но как projCoords.z фиксируется на [0, 1]? Деление на .w ограничивает его до [0,1]? Проблема, с которой я сталкиваюсь, заключается в том, что большая часть сцены затенена, хотя этого не должно быть, например, первый этаж, на следующем изображении (не обращайте внимания на световые артефакты из-за отсутствия смещения - важным моментом является модели горят, а первый этаж нет):

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

const std::string gDirLightFragmentShader =
"#version 430                                                                                                                   \n \
                                                                                                                                \n \
layout(std140) uniform;                                                                                                         \n \
                                                                                                                                \n \
uniform UnifDirLight                                                                                                            \n \
{                                                                                                                               \n \
    mat4 mWVPMatrix;                                                                                                            \n \
    mat4 mVPMatrix;   // light view-projection matrix, pre-multiplied by the bias-matrix                                                                                                          \n \
    vec4 mLightColor;                                                                                                           \n \
    vec4 mLightDir;                                                                                                             \n \
    vec4 mGamma;                                                                                                                \n \
    vec2 mScreenSize;                                                                                                           \n \
} UnifDirLightPass;                                                                                                             \n \
                                                                                                                                \n \
layout (binding = 2) uniform sampler2D unifPositionTexture;                                                                     \n \
layout (binding = 3) uniform sampler2D unifNormalTexture;                                                                       \n \
layout (binding = 4) uniform sampler2D unifDiffuseTexture;                                                                      \n \
layout (binding = 5) uniform sampler2D unifShadowTexture;                                                                       \n \
                                                                                                                                \n \
out vec4 fragColor;                                                                                                             \n \
                                                                                                                                \n \
void main()                                                                                                                     \n \
{                                                                                                                               \n \
    vec2 texcoord = gl_FragCoord.xy / UnifDirLightPass.mScreenSize;                                                             \n \
                                                                                                                                \n \
    vec3 worldPos = texture(unifPositionTexture, texcoord).xyz;                                                                 \n \
    vec3 normal   = normalize(texture(unifNormalTexture, texcoord).xyz);                                                        \n \
    vec3 diffuse  = texture(unifDiffuseTexture, texcoord).xyz;                                                                  \n \
                                                                                                                                \n \
    vec4 lightClipPos = UnifDirLightPass.mVPMatrix * vec4(worldPos, 1.0);                                                      \n \
    vec3 projCoords   = lightClipPos.xyz / lightClipPos.w;                                                                   \n \
                                                                                                                                \n \
    float depthValue = texture(unifShadowTexture, projCoords.xy).x;                                                           \n \
    float visibilty  = 1.0;                                                                                                   \n \
    if (depthValue < (projCoords.z))                                                                                  \n \
         visibilty = 0.0;                                                                                                    \n \
                                                                                                                                \n \
    float angleNormal = clamp(dot(normal, UnifDirLightPass.mLightDir.xyz), 0, 1);                                               \n \
                                                                                                                                \n \
    fragColor = vec4(diffuse, 1.0) * visibilty * angleNormal * UnifDirLightPass.mLightColor;                                 \n \
}                                                                                                                               \n";

person KaiserJohaan    schedule 19.01.2014    source источник


Ответы (1)


Деление на w само по себе не закрепит его до [0,1]. Технически у вас есть координаты пространства отсечения, а затем деление на w преобразует их в пространство NDC. Любая точка x, y или z, равная ‹ -w или > w, обрезается, когда эта операция выполняется с геометрией. Однако, когда вы делаете это с координатами текстуры, вам все равно нужно указать соответствующий режим переноса текстуры (обычно GL_CLAMP_TO_EDGE), потому что координаты не фиксируются автоматически.

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


Кроме того, в этой ситуации sampler2DShadow имеет смысл...

Все это:

float depthValue = texture(unifShadowTexture, projCoords.xy).x;
float visibilty  = 1.0;

if (depthValue < (projCoords.z))
  visibilty = 0.0;            

Можно упростить до этого:

float visibility = texture (unifShadowTexture, projCoords);

При условии, что вы сделаете две вещи:

  1. Объявить unifShadowTexture как sampler2DShadow
  2. Установите GL_TEXTURE_COMPARE_MODE на GL_COMPARE_R_TO_TEXTURE для текстуры глубины.

Это работает, потому что для sampler2DShadow требуются координаты 3D-текстуры. s и t работают как обычно, а r используется для сравнения. Функция сравнения по умолчанию — GL_LESS, поэтому вам не нужно ничего менять в своем коде. Учитывая текстурную функцию GL_NEAREST, возвращаемое значение будет либо 1.0, либо 0.0. Он будет производить то же самое, что и ваш фрагмент кода выше, но с использованием аппаратной функции для фактического теста глубины и часто повышает производительность и дает вам недорогой PCF, если вы включите GL_LINEAR.

Однако я бы посоветовал вам добавить смещение к компоненту Z ваших текстурных координат. Что-то вроде (0,001), или вы получите много «теневых прыщей», слишком большое смещение, и вы получите «петровское панорамирование» (тени, кажется, парят немного над тем, к чему они прикреплены).

person Andon M. Coleman    schedule 19.01.2014
comment
Удивительно, но с использованием Sampler2Dshadow все отлично сработало, большое спасибо. Я не понимаю, почему моя попытка без него не сработала. А что касается смещения, то оно кажется ОЧЕНЬ точным значением, либо вся сцена находится в тени, либо присутствуют прыщи. Есть ли какая-то причина НЕ использовать sampler2Dshadow, например, качество или что-то еще? - person KaiserJohaan; 20.01.2014
comment
Я не могу придумать много причин, чтобы не использовать его для стандартного отображения теней. Существуют более продвинутые алгоритмы, такие как дисперсионное отображение теней, где вы на самом деле не сравниваете одно значение глубины (он сравнивает глубину и глубину*глубину). Для этих алгоритмов вы не можете использовать sampler2DShadow. Но для простого отображения теней я бы всегда рекомендовал использовать sampler2DShadow. У вас случайно уже не установлен GL_TEXTURE_COMPARE_MODE? Неопределенное поведение — сэмплировать текстуру с этим набором, используя sampler2D; это также неопределенное поведение для выборки sampler2DShadow без этого набора. - person Andon M. Coleman; 20.01.2014
comment
@KaiserJohaan: Если вам требуется очень точное значение смещения для работы, у вас, вероятно, есть ближняя / дальняя плоскость, когда вы создали свою карту теней слишком далеко друг от друга. Вы хотите использовать наименьшее возможное расстояние, чтобы максимизировать точность карты теней, обычно вам не нужно где-либо рядом с тем же расстоянием для вашей карты теней, что и остальная часть сцены (за исключением направленных источников света, таких как солнце), потому что освещение падает с расстоянием . - person Andon M. Coleman; 21.01.2014
comment
Спас мой день! Спасибо :) - person Michiel Pater; 04.10.2015