DirectX11 Shadow Mapping Issues

时间:2015-12-10 01:20:29

标签: c++ directx shadow

I'm attempting to add directional shadow mapping to my terrain project, but I'm encountering a few issues. For reference, I'm following the RasterTek shadows tutorial.

The tutorial essentially follows the process of: Create a light > Create a depth texture based on the lights view > Render models and apply shadow shader.

The main issue I'm struggling with is how the tutorial handles the light. It essentially simulates a position and creates an ortho and view matrix. The issue seems to be escalating from how the light is set up. For a simple test, I created a plane, and set the light direction directly down, so everything should be lit, however, the following happens:

enter image description here

And when terrain is generated:

enter image description here Here is code from a few areas that I think would be useful:

Light set up

mLight->SetPosition(XMFLOAT3(10.0f, 30.0f, -0.1f));
mLight->SetLookAt(XMFLOAT3(-10.0f, 0.0f, 0.0f));
mLight->GenerateOthoMatrix(40.0f, 1.0f, 50.0f);

Light GenerateOthoMatrix

void GenerateOthoMatrix(float width, float nearClip, float farClip)
{
    mOrthoMatrix = XMMatrixOrthographicLH(width, width, nearClip, farClip);
}

Light GenerateViewMatrix

XMVECTOR up = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);
XMVECTOR pos = XMLoadFloat3(&mPosition);
XMVECTOR la = XMLoadFloat3(&mLookAt);

mViewMatrix = XMMatrixLookAtLH(pos, la, up);

Depth Render Pass

mRenderTexture->SetRenderTarget(mGraphicsDevice->GetContext());
mRenderTexture->ClearRenderTarget(mGraphicsDevice->GetContext());

mLight->GenerateViewMatrix();

mDepthShader.Info.worldMatrix = mTerrain->GetWorldMatrix();
mDepthShader.Info.viewMatrix = mLight->GetViewMatrix();
mDepthShader.Info.projMatrix = mLight->GetOrthoMatrix();

mTerrain->Render(mGraphicsDevice->GetContext());
mDepthShader.Render(mGraphicsDevice->GetContext(), mTerrain->GetIndexCount());

mGraphicsDevice->ResetBackBuffer();
mGraphicsDevice->ResetViewport();

Shader Render calls simply map the 'info' settings to constant buffers and then call their relative vertex/pixel shaders.

Terrain Render calls just setup Index/Vertex buffers and topology, ready for the shader DrawIndexed.

RenderTexture is essentially a second viewport to render to and get a depth texture from

Main Render Pass

mTerrain->Render(mGraphicsDevice->GetContext());
mLight->GenerateViewMatrix();

mShader.Info.lightProj = mLight->GetOrthoMatrix();
mShader.Info.lightView = mLight->GetViewMatrix();
mShader.Info.depthTex = mRenderTexture->GetSRV();
mShader.Render(mGraphicsDevice->GetContext(), mTerrain->GetIndexCount());

Depth Vertex Shader

cbuffer SPerFrameCB : register(b0)
{
    matrix worldMatrix;
    matrix viewMatrix;
    matrix projMatrix;
};

struct VertexIn
{
    float4 Pos    : POSITION;
};

struct VertexOut
{
    float4 Pos    : SV_POSITION;
    float4 DPos   : TEXTURE0;
};

VertexOut main(VertexIn vin)
{
    VertexOut vout;

    vin.Pos.w = 1.0f;

    vout.Pos = mul(vin.Pos, worldMatrix);
    vout.Pos = mul(vout.Pos, viewMatrix);
    vout.Pos = mul(vout.Pos, projMatrix);

    vout.DPos = vout.Pos;

    return vout;
}

Depth Pixel Shader

struct PixelIn
{
    float4 Pos  : SV_POSITION;
    float4 DPos : TEXTURE0;
};

float4 main(PixelIn pin) : SV_Target

{
    float depthVal = pin.DPos.z / pin.DPos.w;
    float4 colour = float4(depthVal, depthVal, depthVal, 1.0f);

    return colour;
}

Shadow Vertex Shader

cbuffer SPerFrameCB : register(b0)
{
    matrix worldMatrix;
    matrix viewMatrix;
    matrix projMatrix;
    matrix lightViewMatrix;
    matrix lightProjMatrix;
};

struct VertexIn
{
    float4 Pos    : POSITION;
    float2 Tex    : TEXCOORD0;
    float3 Normal : NORMAL;
};

struct VertexOut
{
    float4 Pos    : SV_POSITION;
    float2 Tex    : TEXCOORD0;
    float3 Normal : NORMAL;
    float4 LightV : TEXCOORD1;
};

VertexOut main(VertexIn vin)
{
    VertexOut vout;

    vin.Pos.w = 1.0f;

    float4 worldPos = mul(vin.Pos, worldMatrix);

    vout.Pos = worldPos;
    vout.Pos = mul(vout.Pos, viewMatrix);
    vout.Pos = mul(vout.Pos, projMatrix);

    vout.LightV = worldPos;
    vout.LightV = mul(vout.LightV, lightViewMatrix);
    vout.LightV = mul(vout.LightV, lightProjMatrix);

    vout.Tex = vin.Tex;

    vout.Normal = mul(vin.Normal, (float3x3)worldMatrix);
    vout.Normal = normalize(vout.Normal);

    return vout;
}

Shadow Pixel Shader

Texture2D shaderTexture;

Texture2D lowerTex  : register(t0);
Texture2D mediumTex : register(t1);
Texture2D higherTex : register(t2);
Texture2D depthTex : register(t3);

SamplerState SampleTypeClamp : register(s0);
SamplerState SampleTypeWrap  : register(s1);

cbuffer SPerLightCB : register(b0)
{
    float4 ambientColour;
    float4 diffuseColour;
    float3 lightDirection;
    float padding;
};

struct PixelIn
{
    float4 Pos    : SV_POSITION;
    float2 Tex    : TEXCOORD0;
    float3 Normal : NORMAL;
    float4 LightV : TEXCOORD1;
};

float4 main(PixelIn pin) : SV_Target
{
    float bias = 0.001f;
    float3 lightDir = -lightDirection;
    float4 colour = ambientColour;

    float2 projTexCoord;
    projTexCoord.x =  pin.LightV.x / pin.LightV.w / 2.0f + 0.5f;
    projTexCoord.y = -pin.LightV.y / pin.LightV.w / 2.0f + 0.5f;

    if ((saturate(projTexCoord.x) == projTexCoord.x) && (saturate(projTexCoord.y) == projTexCoord.y))
    {
        float depthVal = depthTex.Sample(SampleTypeClamp, projTexCoord).r;
        float lightDepthVal = pin.LightV.z / pin.LightV.w;
        lightDepthVal -= bias;

        if (lightDepthVal < depthVal)
        {
            float lightIntensity = saturate(dot(pin.Normal, lightDir));

            if (lightIntensity > 0.0f)
            {
                colour += diffuseColour * lightIntensity;
                colour = saturate(colour);
            }
        }
    }

    float4 lowerColour = lowerTex.Sample(SampleTypeWrap, pin.Tex);
    float4 mediumColour = mediumTex.Sample(SampleTypeWrap, pin.Tex);
    float4 higherColour = higherTex.Sample(SampleTypeWrap, pin.Tex);
    float4 texColour;
    float slope = 1.0f - pin.Normal.y, bVal;

    if (slope < 0.4f)
    {
        bVal = slope / 0.4f;
        texColour = lerp(lowerColour, mediumColour, bVal);
    }

    if (slope >= 0.4f && slope < 0.6f)
    {
        bVal = (slope - 0.4f) * (1.0f / (0.6f - 0.4f));
        texColour = lerp(mediumColour, higherColour, bVal);
    }

    if (slope >= 0.6f)
    {
        texColour = higherColour;
    }

    colour *= texColour;

    return colour;
}

I'm very sorry for the large amounts of code - I'm not sure which sections would help best in identifying the issue. If anyone could help, or provide a shadow mapping resource I would be very grateful. There doesn't seem to be many shadow mapping resources, or at least I haven't been able to find many.

2 个答案:

答案 0 :(得分:1)

您的深度着色器看起来很好看。我注意到你的阴影着色器存在差异。我已经完成了DirectX 10&amp;的两个系列。 11到rastertek。我会告诉你我的阴影着色器是什么样的;但是我不记得他们是否从一堂课改为另一堂课。我会把它们发布在这里供你比较。

<强> Shadow.vsh

/////////////////////////////////////////////////
// Filename: shadow.vsh
/////////////////////////////////////////////////

/////////////
// GLOBALS //
/////////////
cbuffer MatrixBuffer
{
    matrix worldMatrix;
    matrix viewMatrix;
    matrix projectionMatrix;
    matrix lightViewMatrix;
    matrix lightProjectionMatrix;
};

//////////////////////
// CONSTANT BUFFERS //
//////////////////////
cbuffer LightBuffer2
{
    float3  lightPosition;
    float   padding;
};

//////////////
// TYPEDFES //
struct VertexInputType
{
    float4 position : POSITION;
    float2 tex : TEXCOORD0;
    float3 normal : NORMAL;
};

struct PixelInputType
{
    float4 position : SV_POSITION;
    float2 tex : TEXCOORD0;
    float3 normal : NORMAL;
    float4 lightViewPosition : TEXCOORD1;
    float3 lightPos : TEXCOORD2;
};

/////////////////////////////////////////////////
// Vertex Shader
/////////////////////////////////////////////////
PixelInputType ShadowVertexShader( VertexInputType input )
{
    PixelInputType output;
    float4 worldPosition;

    // Change The Position Vector To Be 4 Units For Proper Matrix Calculations
    input.position.w = 1.0f;

    // Calculate The Position Of The Vertex Against The World, View And Projection Matrices
    output.position = mul( input.position, worldMatrix );
    output.position = mul( output.position, viewMatrix );
    output.position = mul( output.position, projectionMatrix );

    // Calculate The Position Of The Vertex As Viewed By The Light Source
    output.lightViewPosition = mul( input.position, worldMatrix );
    output.lightViewPosition = mul( output.lightViewPosition, lightViewMatrix );
    output.lightViewPosition = mul( output.lightViewPosition, lightProjectionMatrix );

    // Store The Texture Coordinate For The Pixel Shader
    output.tex = input.tex;

    // Calculate The Normal Vector Against The World Matrix Only
    output.normal = mul( input.normal, (float3x3)worldMatrix );

    // Normalize The Normal Vector
    output.normal = normalize( output.normal );

    // Calculate The Position Of The Vertex In The World
    worldPosition = mul( input.position, worldMatrix );

    // Determine The Light Position Based On The Position Of The Light And The Position Of The Vertex In The World
    output.lightPos = lightPosition.xyz - worldPosition.xyz;

    // Normalize The Light Position Vector
    output.lightPos = normalize( output.lightPos );

    return output;
} // ShadowVertexShader

<强> Shadow.psh

/////////////////////////////////////////////////
// Filename: shadow.ps
/////////////////////////////////////////////////

//////////////
// TEXTURES //
Texture2D depthMapTexture : register(t0);

///////////////////
// SAMPLE STATES //
///////////////////
SamplerState SampleTypeClamp : register(s0);

//////////////
// TYPEDEFS //
//////////////
struct PixelInputType
{
    float4 position : SV_POSITION;
    float2 tex : TEXCOORD0;
    float3 normal : NORMAL;
    float4 lightViewPosition : TEXCOORD1;
    float3 lightPos : TEXCOORD2;
};

/////////////////////////////////////////////////
// Pixel Shader
/////////////////////////////////////////////////
float4 ShadowPixelShader( PixelInputType input ) : SV_TARGET
{
    float   bias;
    float4  color;
    float2  projectTexCoord;
    float   depthValue;
    float   lightDepthValue;
    float   lightIntensity;

    // Set The Bias Value For Fixing The Floating Point Precision Issues
    bias = 0.001f;

    // Set The Default Output Color To Be Black (Shadow)
    color = float4( 0.0f, 0.0f, 0.0f, 1.0f );

    // Calculate The Projected Texture Coordinates
    projectTexCoord.x =  input.lightViewPosition.x / input.lightViewPosition.w / 2.0f + 0.5f;
    projectTexCoord.y = -input.lightViewPosition.y / input.lightViewPosition.w / 2.0f + 0.5f;

    // Determine If The Projected Coordinates Are In The [0,1] Range. If So Then This Pixel Is In The View Of The Light
    if ( (saturate( projectTexCoord.x) == projectTexCoord.x) && (saturate(projectTexCoord.y) == projectTexCoord.y) ) 
    {
        // Sample The Shadow Map Depth Value From The Depth Texture Using The Sampler At The Projected Texture Coordinate Location
        depthValue = depthMapTexture.Sample( SampleTypeClamp, projectTexCoord).r;

        // Calculate The Depth Of The Light
        lightDepthValue = input.lightViewPosition.z / input.lightViewPosition.w;

        // Subtract The Bias From The LightDepthValue
        lightDepthValue = lightDepthValue - bias;

        // Compare The Depth Of The Shadow Map Value And The Depth Of The Light To Determine Whether To Shadow Or To Light This Pixel
        // If The Light Is In Front Of The Object Then Light The Pixel, If Not Then Shadow This Pixel Since An Object (Occluder) Is Casting A Shadow On It
        if ( lightDepthValue < depthValue ) 
        {
            // Calculate The Amount Of Light On This Pixel
            lightIntensity = saturate( dot( input.normal, input.lightPos ) );

            // If This Pixel Is Illuminated Then Set It To Pure White (Non-Shadow)
            if ( lightIntensity > 0.0f )
            {
                // Determine The Final Diffuse Color Based On The Diffuse Color And The Amount Of Light Intensity
                color = float4( 1.0f, 1.0f, 1.0f, 1.0f );           
            }
        }
    }

    return color;
} // ShadowPixelShader

同时确保您的相应.h&amp; .cpp着色器文件匹配着色器中正确的输入和输出结构。在计算像素着色器中的光强度时,您似乎正在使用光线方向而不是光线位置。您的着色器版本中添加的纹理比我更多,但我不认为这会在这种情况下产生差异。我无权查看您的整个解决方案,因此很难分辨出错误的来源。我只希望他能帮助你作为指导。

答案 1 :(得分:0)

我非常清楚这个问题......我只看了一下图片,没看过你的长篇文章。

我认为光线看不到整个场景,只看到被照亮的部分。

有一些解决方案,但没有一个很好。这是一个典型的问题。

尝试增加光透视的截头体。 XMMatrixOrthographicLH需要更大的宽度。增加光的视锥体减少阴影贴图的细节。

没有perfekt解决方案......您需要尝试适合您的问题的值。