Skip to content

Commit 153aa3b

Browse files
committed
Add slang shader for shadow mapping and shadow mapping cascades sample
1 parent 1262317 commit 153aa3b

7 files changed

Lines changed: 436 additions & 8 deletions

File tree

examples/shadowmappingcascade/shadowmappingcascade.cpp

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
/*
22
Vulkan Example - Cascaded shadow mapping for directional light sources
3-
Copyright by Sascha Willems - www.saschawillems.de
3+
Copyright (c) 2016-2025 by Sascha Willems - www.saschawillems.de
44
This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
5-
*/
65
7-
/*
86
This example implements projective cascaded shadow mapping. This technique splits up the camera frustum into
97
multiple frustums with each getting its own full-res shadow map, implemented as a layered depth-only image.
108
The shader then selects the proper shadow map layer depending on what split of the frustum the depth value
@@ -175,7 +173,7 @@ class VulkanExample : public VulkanExampleBase
175173
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr);
176174

177175
// Floor
178-
vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(PushConstBlock), &pushConstBlock);
176+
vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PushConstBlock), &pushConstBlock);
179177
models.terrain.draw(commandBuffer, vkglTF::RenderFlags::BindImages, pipelineLayout);
180178

181179
// Trees
@@ -189,7 +187,7 @@ class VulkanExample : public VulkanExampleBase
189187

190188
for (auto& position : positions) {
191189
pushConstBlock.position = glm::vec4(position, 0.0f);
192-
vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(PushConstBlock), &pushConstBlock);
190+
vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PushConstBlock), &pushConstBlock);
193191
// This will also bind the texture images to set 1
194192
models.tree.draw(commandBuffer, vkglTF::RenderFlags::BindImages, pipelineLayout);
195193
}
@@ -413,7 +411,7 @@ class VulkanExample : public VulkanExampleBase
413411
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.debugShadowMap);
414412
PushConstBlock pushConstBlock = {};
415413
pushConstBlock.cascadeIndex = displayDepthMapCascadeIndex;
416-
vkCmdPushConstants(drawCmdBuffers[i], pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(PushConstBlock), &pushConstBlock);
414+
vkCmdPushConstants(drawCmdBuffers[i], pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PushConstBlock), &pushConstBlock);
417415
vkCmdDraw(drawCmdBuffers[i], 3, 1, 0, 0);
418416
}
419417

@@ -490,7 +488,7 @@ class VulkanExample : public VulkanExampleBase
490488

491489
// Shared pipeline layout (scene and depth map debug display)
492490
{
493-
VkPushConstantRange pushConstantRange = vks::initializers::pushConstantRange(VK_SHADER_STAGE_VERTEX_BIT, sizeof(PushConstBlock), 0);
491+
VkPushConstantRange pushConstantRange = vks::initializers::pushConstantRange(VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(PushConstBlock), 0);
494492
std::array<VkDescriptorSetLayout, 2> setLayouts = { descriptorSetLayout, vkglTF::descriptorSetLayoutImage };
495493
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = vks::initializers::pipelineLayoutCreateInfo(setLayouts.data(), static_cast<uint32_t>(setLayouts.size()));
496494
pipelineLayoutCreateInfo.pushConstantRangeCount = 1;
@@ -500,7 +498,7 @@ class VulkanExample : public VulkanExampleBase
500498

501499
// Depth pass pipeline layout
502500
{
503-
VkPushConstantRange pushConstantRange = vks::initializers::pushConstantRange(VK_SHADER_STAGE_VERTEX_BIT, sizeof(PushConstBlock), 0);
501+
VkPushConstantRange pushConstantRange = vks::initializers::pushConstantRange(VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(PushConstBlock), 0);
504502
std::array<VkDescriptorSetLayout, 2> setLayouts = { descriptorSetLayout, vkglTF::descriptorSetLayoutImage };
505503
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = vks::initializers::pipelineLayoutCreateInfo(setLayouts.data(), static_cast<uint32_t>(setLayouts.size()));
506504
pipelineLayoutCreateInfo.pushConstantRangeCount = 1;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/* Copyright (c) 2025, Sascha Willems
2+
*
3+
* SPDX-License-Identifier: MIT
4+
*
5+
*/
6+
7+
struct UBO
8+
{
9+
float4x4 depthMVP;
10+
};
11+
ConstantBuffer<UBO> ubo;
12+
13+
[shader("vertex")]
14+
float4 vertexMain(float3 Pos) : SV_POSITION
15+
{
16+
return mul(ubo.depthMVP, float4(Pos, 1.0));
17+
}
18+
19+
[shader("fragment")]
20+
float4 fragmentMain()
21+
{
22+
return float4(1.0, 0.0, 0.0, 1.0);
23+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/* Copyright (c) 2025, Sascha Willems
2+
*
3+
* SPDX-License-Identifier: MIT
4+
*
5+
*/
6+
7+
struct UBO
8+
{
9+
float4x4 projection;
10+
float4x4 view;
11+
float4x4 model;
12+
float4x4 lightSpace;
13+
float4 lightPos;
14+
float zNear;
15+
float zFar;
16+
};
17+
ConstantBuffer<UBO> ubo;
18+
Sampler2D samplerColor;
19+
20+
float LinearizeDepth(float depth)
21+
{
22+
float n = ubo.zNear;
23+
float f = ubo.zFar;
24+
float z = depth;
25+
return (2.0 * n) / (f + n - z * (f - n));
26+
}
27+
28+
struct VSOutput
29+
{
30+
float4 Pos : SV_POSITION;
31+
float2 UV;
32+
};
33+
34+
[shader("vertex")]
35+
VSOutput vertexMain(uint VertexIndex: SV_VertexID)
36+
{
37+
VSOutput output;
38+
output.UV = float2((VertexIndex << 1) & 2, VertexIndex & 2);
39+
output.Pos = float4(output.UV * 2.0f - 1.0f, 0.0f, 1.0f);
40+
return output;
41+
}
42+
43+
[shader("fragment")]
44+
float4 fragmentMain(VSOutput input)
45+
{
46+
float depth = samplerColor.Sample(input.UV).r;
47+
return float4((1.0 - LinearizeDepth(depth)).xxx, 1.0);
48+
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/* Copyright (c) 2025, Sascha Willems
2+
*
3+
* SPDX-License-Identifier: MIT
4+
*
5+
*/
6+
7+
struct VSInput
8+
{
9+
float3 Pos;
10+
float2 UV;
11+
float3 Color;
12+
float3 Normal;
13+
};
14+
15+
struct VSOutput
16+
{
17+
float4 Pos : SV_POSITION;
18+
float3 Normal;
19+
float3 Color;
20+
float3 ViewVec;
21+
float3 LightVec;
22+
float4 ShadowCoord;
23+
};
24+
25+
struct UBO
26+
{
27+
float4x4 projection;
28+
float4x4 view;
29+
float4x4 model;
30+
float4x4 lightSpace;
31+
float4 lightPos;
32+
float zNear;
33+
float zFar;
34+
};
35+
ConstantBuffer<UBO> ubo;
36+
Sampler2D shadowMapSampler;
37+
38+
[SpecializationConstant] const int enablePCF = 0;
39+
40+
#define ambient 0.1
41+
42+
float textureProj(float4 shadowCoord, float2 off)
43+
{
44+
float shadow = 1.0;
45+
if ( shadowCoord.z > -1.0 && shadowCoord.z < 1.0 )
46+
{
47+
float dist = shadowMapSampler.Sample(shadowCoord.xy + off).r;
48+
if ( shadowCoord.w > 0.0 && dist < shadowCoord.z )
49+
{
50+
shadow = ambient;
51+
}
52+
}
53+
return shadow;
54+
}
55+
56+
float filterPCF(float4 sc)
57+
{
58+
int2 texDim;
59+
shadowMapSampler.GetDimensions(texDim.x, texDim.y);
60+
float scale = 1.5;
61+
float dx = scale * 1.0 / float(texDim.x);
62+
float dy = scale * 1.0 / float(texDim.y);
63+
64+
float shadowFactor = 0.0;
65+
int count = 0;
66+
int range = 1;
67+
68+
for (int x = -range; x <= range; x++)
69+
{
70+
for (int y = -range; y <= range; y++)
71+
{
72+
shadowFactor += textureProj(sc, float2(dx*x, dy*y));
73+
count++;
74+
}
75+
76+
}
77+
return shadowFactor / count;
78+
}
79+
80+
static const float4x4 biasMat = float4x4(
81+
0.5, 0.0, 0.0, 0.5,
82+
0.0, 0.5, 0.0, 0.5,
83+
0.0, 0.0, 1.0, 0.0,
84+
0.0, 0.0, 0.0, 1.0);
85+
86+
[shader("vertex")]
87+
VSOutput vertexMain(VSInput input)
88+
{
89+
VSOutput output = (VSOutput)0;
90+
output.Color = input.Color;
91+
output.Normal = input.Normal;
92+
93+
output.Pos = mul(ubo.projection, mul(ubo.view, mul(ubo.model, float4(input.Pos.xyz, 1.0))));
94+
95+
float4 pos = mul(ubo.model, float4(input.Pos, 1.0));
96+
output.Normal = mul((float3x3)ubo.model, input.Normal);
97+
output.LightVec = normalize(ubo.lightPos.xyz - input.Pos);
98+
output.ViewVec = -pos.xyz;
99+
100+
output.ShadowCoord = mul(biasMat, mul(ubo.lightSpace, mul(ubo.model, float4(input.Pos, 1.0))));
101+
return output;
102+
}
103+
104+
[shader("fragment")]
105+
float4 fragmentMain(VSOutput input)
106+
{
107+
float shadow = (enablePCF == 1) ? filterPCF(input.ShadowCoord / input.ShadowCoord.w) : textureProj(input.ShadowCoord / input.ShadowCoord.w, float2(0.0, 0.0));
108+
109+
float3 N = normalize(input.Normal);
110+
float3 L = normalize(input.LightVec);
111+
float3 V = normalize(input.ViewVec);
112+
float3 R = normalize(-reflect(L, N));
113+
float3 diffuse = max(dot(N, L), ambient) * input.Color;
114+
115+
return float4(diffuse * shadow, 1.0);
116+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/* Copyright (c) 2025, Sascha Willems
2+
*
3+
* SPDX-License-Identifier: MIT
4+
*
5+
*/
6+
7+
[[vk::binding(1,0)]] Sampler2DArray shadowMapSampler;
8+
9+
struct VSOutput
10+
{
11+
float4 Pos : SV_POSITION;
12+
float2 UV;
13+
};
14+
15+
[shader("vertex")]
16+
VSOutput vertexMain(uint VertexIndex: SV_VertexID)
17+
{
18+
VSOutput output;
19+
output.UV = float2((VertexIndex << 1) & 2, VertexIndex & 2);
20+
output.Pos = float4(output.UV * 2.0f - 1.0f, 0.0f, 1.0f);
21+
return output;
22+
}
23+
24+
[shader("fragment")]
25+
float4 fragmentMain(VSOutput input, uniform float4 meshPosition, uniform uint cascadeIndex)
26+
{
27+
float depth = shadowMapSampler.Sample(float3(input.UV, float(cascadeIndex))).r;
28+
return float4(depth.xxx, 1.0);
29+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/* Copyright (c) 2025, Sascha Willems
2+
*
3+
* SPDX-License-Identifier: MIT
4+
*
5+
*/
6+
7+
struct VSInput
8+
{
9+
float3 Pos;
10+
float2 UV;
11+
};
12+
13+
struct VSOutput
14+
{
15+
float4 Pos : SV_POSITION;
16+
float2 UV;
17+
};
18+
19+
// todo: pass via specialization constant
20+
#define SHADOW_MAP_CASCADE_COUNT 4
21+
22+
struct UBO {
23+
float4x4 cascadeViewProjMat[SHADOW_MAP_CASCADE_COUNT];
24+
};
25+
[[vk::binding(3, 0)]] ConstantBuffer<UBO> ubo;
26+
27+
[[vk::binding(0, 1)]] Sampler2D colorMapSampler : register(s0, space1);
28+
29+
[shader("vertex")]
30+
VSOutput vertexMain(VSInput input, uniform float4 meshPosition, uniform uint cascadeIndex)
31+
{
32+
VSOutput output;
33+
output.UV = input.UV;
34+
float3 pos = input.Pos + meshPosition.xyz;
35+
output.Pos = mul(ubo.cascadeViewProjMat[cascadeIndex], float4(pos, 1.0));
36+
return output;
37+
}
38+
39+
[shader("fragment")]
40+
void fragmentMain(VSOutput input)
41+
{
42+
float alpha = colorMapSampler.Sample(input.UV).a;
43+
if (alpha < 0.5) {
44+
clip(-1);
45+
}
46+
}

0 commit comments

Comments
 (0)