【DirectX】44-定向光阴影映射

参考教程

Tutorial 48: Directional Shadow Maps

学习记录

  这篇文章中我们介绍对于定向光的阴影映射,本篇代码基于阴影映射篇,修改不多。

  定向光(平行光)的阴影映射和之前不同之处在于它是基于正交投影而非透视投影的,也即是说它不会有近小远大的效果,我们将以正交投影来渲染深度图使得我们的阴影是正交投影化的。

  首先我们修改 shadow.vsshadow.ps ,现在我们不需要光的坐标而是使用定向光的方向计算,所以在 shadow.vs 里不再需要第二个缓冲,也不用进行多余的计算。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
////////////////////////////////////////////////////////////////////////////////
// Filename: shadow.vs
////////////////////////////////////////////////////////////////////////////////


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

//////////////
// TYPEDEFS //
//////////////
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;
};


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

// 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 vertice 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 coordinates 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);

return output;
}

  在 shadow.ps 里我们基于传进来的光方向进行与法线的点乘:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
////////////////////////////////////////////////////////////////////////////////
// Filename: shadow.ps
////////////////////////////////////////////////////////////////////////////////


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


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


//////////////////////
// CONSTANT BUFFERS //
//////////////////////
cbuffer LightBuffer
{
float4 ambientColor;
float4 diffuseColor;
float3 lightDirection;
float padding;
};
//////////////
// TYPEDEFS //
//////////////
struct PixelInputType
{
float4 position : SV_POSITION;
float2 tex : TEXCOORD0;
float3 normal : NORMAL;
float4 lightViewPosition : TEXCOORD1;
};
////////////////////////////////////////////////////////////////////////////////
// Pixel Shader
////////////////////////////////////////////////////////////////////////////////
float4 ShadowPixelShader(PixelInputType input) : SV_TARGET
{
float bias;
float4 color;
float2 projectTexCoord;
float depthValue;
float lightDepthValue;
float lightIntensity;
float4 textureColor;
float3 lightDir;

lightDir = -lightDirection;

// Set the bias value for fixing the floating point precision issues.
bias = 0.001f;

// Set the default output color to the ambient light value for all pixels.
color = ambientColor;

// 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 to 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, lightDir));

if(lightIntensity > 0.0f)
{
// Determine the final diffuse color based on the diffuse color and the amount of light intensity.
color += (diffuseColor * lightIntensity);

// Saturate the final light color.
color = saturate(color);
}
}
} else {
// If this is outside the area of shadow map range then draw things normally with regular lighting.
lightIntensity = saturate(dot(input.normal, lightDir));
if(lightIntensity > 0.0f)
{
color += (diffuseColor * lightIntensity);
color = saturate(color);
}
}
// Sample the pixel color from the texture using the sampler at this texture coordinate location.
textureColor = shaderTexture.Sample(SampleTypeWrap, input.tex);

// Combine the light and texture color.
color = color * textureColor;

return color;
}

  ShadowShaderClass 被修改为适应于新着色器的渲染类,其声明如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
////////////////////////////////////////////////////////////////////////////////
// Class name: ShadowShaderClass
////////////////////////////////////////////////////////////////////////////////
class ShadowShaderClass
{
private:
struct MatrixBufferType
{
DirectX::XMMATRIX world;
DirectX::XMMATRIX view;
DirectX::XMMATRIX projection;
DirectX::XMMATRIX lightView;
DirectX::XMMATRIX lightProjection;
};

struct LightBufferType
{
DirectX::XMFLOAT4 ambientColor;
DirectX::XMFLOAT4 diffuseColor;
DirectX::XMFLOAT3 lightDirection;
float padding;
};

public:
ShadowShaderClass();
ShadowShaderClass(const ShadowShaderClass&);
~ShadowShaderClass();

bool Initialize(ID3D11Device*, HWND);
void Shutdown();
bool Render(ID3D11DeviceContext*, int, DirectX::XMMATRIX, DirectX::XMMATRIX, DirectX::XMMATRIX, DirectX::XMMATRIX, DirectX::XMMATRIX, ID3D11ShaderResourceView*,
ID3D11ShaderResourceView*, DirectX::XMFLOAT3, DirectX::XMFLOAT4, DirectX::XMFLOAT4);

private:
bool InitializeShader(ID3D11Device*, HWND, CHAR*, CHAR*);
void ShutdownShader();
void OutputShaderErrorMessage(ID3D10Blob*, HWND, CHAR*);

bool SetShaderParameters(ID3D11DeviceContext*, DirectX::XMMATRIX, DirectX::XMMATRIX, DirectX::XMMATRIX, DirectX::XMMATRIX, DirectX::XMMATRIX, ID3D11ShaderResourceView*,
ID3D11ShaderResourceView*, DirectX::XMFLOAT3, DirectX::XMFLOAT4, DirectX::XMFLOAT4);
void RenderShader(ID3D11DeviceContext*, int);

private:
ID3D11VertexShader* m_vertexShader;
ID3D11PixelShader* m_pixelShader;
ID3D11InputLayout* m_layout;
ID3D11SamplerState* m_sampleStateWrap;
ID3D11SamplerState* m_sampleStateClamp;
ID3D11Buffer* m_matrixBuffer;
ID3D11Buffer* m_lightBuffer;
};

  现在我们需要定向光以及正交投影,所以我们在 LightClass 里添加这两个的对象以及生成方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
////////////////////////////////////////////////////////////////////////////////
// Class name: LightClass
////////////////////////////////////////////////////////////////////////////////
class LightClass
{
public:
...
void GenerateOrthoMatrix(float, float, float);
void GetOrthoMatrix(DirectX::XMMATRIX&);

void SetDirection(float, float, float);
DirectX::XMFLOAT3 GetDirection();

...
private:
...
DirectX::XMMATRIX m_orthoMatrix;
DirectX::XMFLOAT3 m_lightDirection;
};

  最后,我们使用它的正交矩阵来渲染深度图和最终效果,部分代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
bool GraphicsClass::Frame(float posX, float posY, float posZ, float rotX, float rotY, float rotZ)
{
bool result;
static float lightAngle = 270.0f;
float radians;
static float lightPosX = 9.0f;


// Set the position of the camera.
m_Camera->SetPosition(posX, posY, posZ);
m_Camera->SetRotation(rotX, rotY, rotZ);


lightPosX -= 0.003f;

// Update the angle of the light each frame.
lightAngle -= 0.03f;
if(lightAngle < 90.0f)
{
lightAngle = 270.0f;

// Reset the light position also.
lightPosX = 9.0f;
}
radians = lightAngle * 0.0174532925f;

// Update the direction of the light.
m_Light->SetDirection(sinf(radians), cosf(radians), 0.0f);
m_Light->SetPosition(lightPosX, 8.0f, -1.0f);
m_Light->SetLookAt(0.0f, 0.0f, 0.0f);

// Render the graphics scene.
result = Render();
if(!result)
{
return false;
}

return true;
}


bool GraphicsClass::RenderSceneToTexture()
{
DirectX::XMMATRIX worldMatrix, lightViewMatrix, lightOrthoMatrix;
float posX, posY, posZ;
bool result;


// Set the render target to be the render to texture.
m_RenderTexture->SetRenderTarget(m_D3D->GetDeviceContext());

// Clear the render to texture.
m_RenderTexture->ClearRenderTarget(m_D3D->GetDeviceContext(), 0.0f, 0.0f, 0.0f, 1.0f);

// Generate the light view matrix based on the light's position.
m_Light->GenerateViewMatrix();

// Get the world matrix from the d3d object.
m_D3D->GetWorldMatrix(worldMatrix);

// Get the view and orthographic matrices from the light object.
m_Light->GetViewMatrix(lightViewMatrix);
m_Light->GetOrthoMatrix(lightOrthoMatrix);

// Setup the translation matrix for the cube model.
m_CubeModel->GetPosition(posX, posY, posZ);
worldMatrix *= DirectX::XMMatrixTranslation(posX, posY, posZ);

// Render the cube model with the depth shader.
m_CubeModel->Render(m_D3D->GetDeviceContext());
result = m_DepthShader->Render(m_D3D->GetDeviceContext(), m_CubeModel->GetIndexCount(), worldMatrix, lightViewMatrix, lightOrthoMatrix);
if(!result)
{
return false;
}

// Reset the world matrix.
m_D3D->GetWorldMatrix(worldMatrix);

// Setup the translation matrix for the sphere model.
m_SphereModel->GetPosition(posX, posY, posZ);
worldMatrix *= DirectX::XMMatrixTranslation(posX, posY, posZ);

// Render the sphere model with the depth shader.
m_SphereModel->Render(m_D3D->GetDeviceContext());
result = m_DepthShader->Render(m_D3D->GetDeviceContext(), m_SphereModel->GetIndexCount(), worldMatrix, lightViewMatrix, lightOrthoMatrix);
if(!result)
{
return false;
}

// Reset the world matrix.
m_D3D->GetWorldMatrix(worldMatrix);

// Setup the translation matrix for the ground model.
m_GroundModel->GetPosition(posX, posY, posZ);
worldMatrix *= DirectX::XMMatrixTranslation(posX, posY, posZ);

// Render the ground model with the depth shader.
m_GroundModel->Render(m_D3D->GetDeviceContext());
result = m_DepthShader->Render(m_D3D->GetDeviceContext(), m_GroundModel->GetIndexCount(), worldMatrix, lightViewMatrix, lightOrthoMatrix);
if(!result)
{
return false;
}

// Reset the render target back to the original back buffer and not the render to texture anymore.
m_D3D->SetBackBufferRenderTarget();

// Reset the viewport back to the original.
m_D3D->ResetViewport();

return true;
}


bool GraphicsClass::Render()
{
DirectX::XMMATRIX worldMatrix, viewMatrix, projectionMatrix;
DirectX::XMMATRIX lightViewMatrix, lightOrthoMatrix;
bool result;
float posX, posY, posZ;


// First render the scene to a texture.
result = RenderSceneToTexture();
if(!result)
{
return false;
}

// Clear the buffers to begin the scene.
m_D3D->BeginScene(0.0f, 0.0f, 0.0f, 1.0f);

// Generate the view matrix based on the camera's position.
m_Camera->Render();

// Generate the light view matrix based on the light's position.
m_Light->GenerateViewMatrix();

// Get the world, view, and projection matrices from the camera and d3d objects.
m_Camera->GetViewMatrix(viewMatrix);
m_D3D->GetWorldMatrix(worldMatrix);
m_D3D->GetProjectionMatrix(projectionMatrix);

// Get the light's view and projection matrices from the light object.
m_Light->GetViewMatrix(lightViewMatrix);
m_Light->GetOrthoMatrix(lightOrthoMatrix);

// Setup the translation matrix for the cube model.
m_CubeModel->GetPosition(posX, posY, posZ);
worldMatrix *= DirectX::XMMatrixTranslation(posX, posY, posZ);

// Put the cube model vertex and index buffers on the graphics pipeline to prepare them for drawing.
m_CubeModel->Render(m_D3D->GetDeviceContext());

// Render the model using the shadow shader.
result = m_ShadowShader->Render(m_D3D->GetDeviceContext(), m_CubeModel->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix, lightViewMatrix,
lightOrthoMatrix, m_CubeModel->GetTexture(), m_RenderTexture->GetShaderResourceView(), m_Light->GetDirection(),
m_Light->GetAmbientColor(), m_Light->GetDiffuseColor());
if(!result)
{
return false;
}

// Reset the world matrix.
m_D3D->GetWorldMatrix(worldMatrix);

// Setup the translation matrix for the sphere model.
m_SphereModel->GetPosition(posX, posY, posZ);
worldMatrix *= DirectX::XMMatrixTranslation(posX, posY, posZ);

// Put the model vertex and index buffers on the graphics pipeline to prepare them for drawing.
m_SphereModel->Render(m_D3D->GetDeviceContext());
result = m_ShadowShader->Render(m_D3D->GetDeviceContext(), m_SphereModel->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix, lightViewMatrix,
lightOrthoMatrix, m_SphereModel->GetTexture(), m_RenderTexture->GetShaderResourceView(), m_Light->GetDirection(),
m_Light->GetAmbientColor(), m_Light->GetDiffuseColor());
if(!result)
{
return false;
}

// Reset the world matrix.
m_D3D->GetWorldMatrix(worldMatrix);

// Setup the translation matrix for the ground model.
m_GroundModel->GetPosition(posX, posY, posZ);
worldMatrix *= DirectX::XMMatrixTranslation(posX, posY, posZ);

// Render the ground model using the shadow shader.
m_GroundModel->Render(m_D3D->GetDeviceContext());
result = m_ShadowShader->Render(m_D3D->GetDeviceContext(), m_GroundModel->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix, lightViewMatrix,
lightOrthoMatrix, m_GroundModel->GetTexture(), m_RenderTexture->GetShaderResourceView(), m_Light->GetDirection(),
m_Light->GetAmbientColor(), m_Light->GetDiffuseColor());
if(!result)
{
return false;
}

// Present the rendered scene to the screen.
m_D3D->EndScene();

return true;
}

  最终效果:

1

  源代码:DX11Tutorial-DirectionalShadowMaps

0%