(Monogame / HLSL)阴影映射问题-阴影取决于相机位置

时间:2019-01-31 23:10:40

标签: c# shadow monogame hlsl deferred

我在这个问题上花了很长时间,终于意识到我需要认真的帮助...

所以基本上我想在Monogame中通过我的项目实现适当的阴影。为此,我使用了多个教程(主要针对旧的XNA)在HLSL中编写了一个延迟的dhader。 问题是,尽管我的照明和阴影可以用作聚光灯,但场景中的光非常依赖于我的相机,如您在图像中看到的:https://imgur.com/a/TU7y0bs

我尝试了许多不同的方法来解决此问题:

  1. 更大的DepthBias会通过大规模平移来扩大“无阴影”的半径,并且所描述的问题根本没有解决。
  2. 有一篇论文建议使用指数阴影贴图,但我根本不喜欢结果,因为光的流血令人难以忍受,而且较小的阴影(如墙上火炬后面的阴影)无法渲染。
  3. li>
  4. 我将GBuffer DepthMap切换为1-z / w,以获得更高的精度,但这也无法解决问题。

我正在使用

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

从灯光角度存储深度。

我使用此PixelShader函数计算阴影: 请注意,我希望将来将此着色器调整为点光源-这就是为什么我直接使用length(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)

我希望图片中显示的伪像也能够使代码适应点光源。

这可能是我从屏幕空间检索世界位置并且深度达到低分辨率的方式有问题吗?

非常感谢您的帮助!

---更新---

我更改了“ Lighting”着色器,以显示存储在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]的近平面和远平面,我发现这很奇怪),或者方式有问题我正在从我的DepthMap中恢复给定像素的worldPosition。

1 个答案:

答案 0 :(得分:0)

我终于找到了解决方案,如果有人偶然发现同一问题,我将其发布在这里:

我使用的教程是XNA / DX9的。但是,在以DX10 +为目标时,需要做一点微小的改变:

在XNA / DX9中,UV坐标未与实际像素对齐,需要对齐。这就是index($0,"SEARCH")==1- float2(1.0f / GBufferTextureSize.xy);的目的。在DX10及更高版本中不需要此功能,这将导致我遇到的问题。

解决方案:

用于全屏四边形的UV坐标:
对于XNA / DX9:

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) - float2(1.0f / GBufferTextureSize.xy);