(Monogame / HLSL) Проблемы с ShadowMapping - тень зависит от положения камеры - PullRequest
0 голосов
/ 01 февраля 2019

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

Так что в основном я хотел внедрить подходящие тени в свой проект, который я пишу в Monogame.Для этого я написал отложенный дхадер в HLSL, используя несколько учебных пособий, в основном для старой XNA.Проблема в том, что, хотя мое освещение и тень работают на прожектор, свет на полу моей сцены очень зависит от моей камеры, как вы можете видеть на изображениях: https://imgur.com/a/TU7y0bs

Я пробовал многоразные вещи, чтобы решить эту проблему:

  1. Больший DepthBias будет расширять радиус "без тени" с массивным панорамированием Питера, и описанная проблема не будет решена вообще.
  2. OneВ статье предлагалось использовать экспоненциальную карту теней, но мне совсем не понравились результаты, поскольку легкое кровотечение было невыносимым, а тени меньшего размера (например, за факелом у стены) не рендерились.
  3. Я переключил свой GBuffer DepthMap на 1-z / w, чтобы получить больше точности, но это также не решило проблему.

Я использую

new RenderTarget2D(device,
                Width, Height, false, SurfaceFormat.Vector2, DepthFormat.Depth24Stencil8)

для храненияглубина с точки зрения освещения.

Я вычисляю тень, используя эту функцию PixelShader: обратите внимание, что я хочу адаптировать этот шейдер к точечному освещению в будущем - вот почему я симслой с использованием длины (LightPos - PixelPos).SpotLight.fx - PixelShader

float4 PS(VSO input) : SV_TARGET0
{

    // Fancy Lighting equations

input.ScreenPosition.xy /= input.ScreenPosition.w;
float2 UV = 0.5f * (float2(input.ScreenPosition.x, -input.ScreenPosition.y) + 1) - float2(1.0f / GBufferTextureSize.xy);

// Sample Depth from DepthMap
float Depth = DepthMap.Sample(SampleTypeClamp, UV).x;

// Getting the PixelPosition in WorldSpace
float4 Position = 1.0f;
Position.xy = input.ScreenPosition.xy;
Position.z = Depth;

// Transform Position to WorldSpace
Position = mul(Position, InverseViewProjection);
Position /= Position.w;

float4 LightScreenPos = mul(Position, LightViewProjection);
LightScreenPos /= LightScreenPos.w;


// Calculate Projected UV from Light POV -> ScreenPos is in [-1;1] Space
float2 LightUV = 0.5f * (float2(LightScreenPos.x, -LightScreenPos.y) + 1.0f);
float lightDepth = ShadowMap.Sample(SampleDot, LightUV).r;

// Linear depth model
float closestDepth = lightDepth * LightFarplane; // Depth is stored in [0, 1]; bring it to [0, farplane]
float currentDepth = length(LightPosition.xyz - Position.xyz) - DepthBias;
ShadowFactor = step(currentDepth, closestDepth); // closestDepth > currentDepth -> Occluded, Shadow.

float4 phong = Phong(...); 
return ShadowFactor * phong;
}

LightViewProjection - это просто light.View * light.Projection InverseViewProjection - это Matrix.Invert(camera.View * Camera.Projection) Phong () - это функция, которую я вызываю для завершения освещения. LightDepthMap просто хранит length(lightPos - Position)

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

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

Помощь очень важна!

--- Обновление ---

Я изменил свой шейдер освещения, чтобы отобразить разницу между расстояниемхранится в shadowMap и рассчитывается расстояние на месте в Pixelshader:

float4 PixelShaderFct(...) : SV_TARGET0
{
    // Get Depth from Texture

    float4 Position = 1.0f;
    Position.xy = input.ScreenPosition.xy;
    Position.z = Depth;
    Position = mul(Position, InverseViewProjection);
    Position /= Position.w; 

    float4 LightScreenPos = mul(Position, LightViewProjection);
    LightScreenPos /= LightScreenPos.w; 

    // Calculate Projected UV from Light POV -> ScreenPos is in [-1;1] Space
    float2 LUV = 0.5f * (float2(LightScreenPos.x, -LightScreenPos.y) + 1.0f);
    float lightZ = ShadowMap.Sample(SampleDot, LUV).r;

    float Attenuation = AttenuationMap.Sample(SampleType, LUV).r;

    float ShadowFactor = 1;
    // Linear depth model; lightZ stores (LightPos - Pos)/LightFarPlane
    float closestDepth = lightZ * LightFarPlane;
    float currentDepth = length(LightPosition.xyz - Position.xyz) -DepthBias;
    return (closestDepth - currentDepth);
}

Поскольку я в основном выводю Length - (Length - Bias), можно ожидать, что в качестве цвета будет использоваться изображение с DepthBias.,Но это не тот результат, который я получаю:

https://imgur.com/a/4PXLH7s

Исходя из этого, я предполагаю, что либо у меня есть проблемы с точностью (что я нахожу странным,учитывая, что я работаю с ближними и дальними плоскостями [0.1, 50]), или что-то не так с тем, как я восстанавливаю worldPosition данного пикселя из моей карты глубины.

1 Ответ

0 голосов
/ 03 февраля 2019

Я наконец нашел решение и выкладываю его здесь, если кто-то сталкивается с той же проблемой:

Учебное пособие, которое я использовал, было для XNA / DX9.Но так как я нацеливаюсь на DX10 +, необходимо сделать небольшое изменение:

В XNA / DX9 координаты UV не совпадают с фактическими пикселями и должны быть выровнены.Вот для чего - float2(1.0f / GBufferTextureSize.xy); в float2 UV = 0.5f * (float2(input.ScreenPosition.x, -input.ScreenPosition.y) + 1) - float2(1.0f / GBufferTextureSize.xy);.Это НЕ требуется в DX10 и выше и приведет к проблеме, с которой я столкнулся.

Решение:

УФ-координаты для полноэкранного четырехугольника:
Для XNA / DX9:

input.ScreenPosition.xy /= input.ScreenPosition.w;
float2 UV = 0.5f * (float2(input.ScreenPosition.x, -input.ScreenPosition.y) + 1) - float2(1.0f / GBufferTextureSize.xy);

Для Monogame / DX10 +

input.ScreenPosition.xy /= input.ScreenPosition.w;
float2 UV = 0.5f * (float2(input.ScreenPosition.x, -input.ScreenPosition.y) + 1)
...