Skip to content

Commit 1c151f1

Browse files
committed
getting closer- there's now a shadow map
1 parent 880e37b commit 1c151f1

File tree

7 files changed

+157
-7
lines changed

7 files changed

+157
-7
lines changed

src/vulkan/Buffer.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,5 +43,15 @@ namespace reactor {
4343
}
4444
}
4545

46+
void* Buffer::map() {
47+
void* data;
48+
vmaMapMemory(m_allocator.getAllocator(), m_allocation, &data);
49+
return data;
50+
}
51+
52+
void Buffer::unmap() {
53+
vmaUnmapMemory(m_allocator.getAllocator(), m_allocation);
54+
}
55+
4656

4757
} // reactor

src/vulkan/Buffer.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ class Buffer {
2222
[[nodiscard]] VmaAllocation allocation() const { return m_allocation; }
2323
[[nodiscard]] vk::DeviceSize size() const { return m_size; }
2424

25-
25+
void* map();
26+
void unmap();
2627

2728
private:
2829
Allocator& m_allocator;

src/vulkan/Pipeline.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,12 @@ namespace reactor
8787
return *this;
8888
}
8989

90+
Pipeline::Builder& Pipeline::Builder::enableDepthBias(bool enable)
91+
{
92+
m_depthBiasEnable = enable;
93+
return *this;
94+
}
95+
9096

9197
std::unique_ptr<Pipeline> Pipeline::Builder::build() const
9298
{
@@ -120,7 +126,7 @@ namespace reactor
120126

121127
// 5. Rasterizer
122128
vk::PipelineRasterizationStateCreateInfo rasterizer(
123-
{}, VK_FALSE, VK_FALSE, vk::PolygonMode::eFill, m_cullMode, m_frontFace, VK_FALSE, 0.0f, 0.0f, 0.0f, 1.0f);
129+
{}, VK_FALSE, VK_FALSE, vk::PolygonMode::eFill, m_cullMode, m_frontFace, m_depthBiasEnable? VK_TRUE : VK_FALSE, 0.0f, 0.0f, 0.0f, 1.0f);
124130

125131
// 6. Multisampling
126132
vk::PipelineMultisampleStateCreateInfo multisampling({}, utils::mapSampleCountFlag(m_samples), VK_FALSE);
@@ -149,6 +155,12 @@ namespace reactor
149155
// 9. Dynamic State
150156
std::vector<vk::DynamicState> dynamicStates = {vk::DynamicState::eViewport,
151157
vk::DynamicState::eScissor};
158+
// conditionally add eDepthBias to the list of dynamic states
159+
if (m_depthBiasEnable)
160+
{
161+
dynamicStates.push_back(vk::DynamicState::eDepthBias);
162+
}
163+
152164
vk::PipelineDynamicStateCreateInfo dynamicState({}, dynamicStates);
153165

154166
// 10. Pipeline Layout

src/vulkan/Pipeline.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ namespace reactor
2727
Builder& setCullMode(vk::CullModeFlags cullMode);
2828
Builder& setFrontFace(vk::FrontFace frontFace);
2929
Builder& addPushContantRange(vk::ShaderStageFlags stages, uint32_t offset, uint32_t size);
30+
Builder& enableDepthBias(bool enable=true);
3031

3132
[[nodiscard]] std::unique_ptr<Pipeline> build() const;
3233

@@ -41,6 +42,7 @@ namespace reactor
4142
uint32_t m_samples = 1;
4243
vk::CullModeFlags m_cullMode = vk::CullModeFlagBits::eBack;
4344
vk::FrontFace m_frontFace = vk::FrontFace::eCounterClockwise;
45+
bool m_depthBiasEnable = false;
4446

4547
std::vector<vk::VertexInputBindingDescription> m_bindings;
4648
std::vector<vk::VertexInputAttributeDescription> m_attributes;

src/vulkan/ShadowMapping.cpp

Lines changed: 85 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@
55
namespace reactor
66
{
77

8+
struct SceneUBO
9+
{
10+
glm::mat4 view;
11+
glm::mat4 projection;
12+
};
13+
814
ShadowMapping::ShadowMapping(VulkanRenderer& renderer, uint32_t resolution)
915
: m_renderer(renderer), m_resolution(resolution), m_shadowMapView(VK_NULL_HANDLE),
1016
m_shadowMapSampler(VK_NULL_HANDLE)
@@ -76,7 +82,11 @@ void ShadowMapping::createResources()
7682
m_mvpBuffer.reserve(frameCount);
7783
for (size_t i = 0; i < frameCount; ++i)
7884
{
79-
m_mvpBuffer.push_back(std::make_unique<Buffer>(m_renderer.allocator(), sizeof(glm::mat4), vk::BufferUsageFlagBits::eUniformBuffer, VMA_MEMORY_USAGE_AUTO, "MVP Buffer"));
85+
m_mvpBuffer.push_back(std::make_unique<Buffer>(m_renderer.allocator(),
86+
sizeof(SceneUBO),
87+
vk::BufferUsageFlagBits::eUniformBuffer,
88+
VMA_MEMORY_USAGE_CPU_TO_GPU,
89+
"MVP Buffer"));
8090
}
8191
}
8292

@@ -99,8 +109,10 @@ void ShadowMapping::createPipeline()
99109
// No fragment shader, we only want depth output
100110
.setVertexInputFromVertex()
101111
.setDepthAttachment(vk::Format::eD32Sfloat, true) // depth test and write enabled
112+
.enableDepthBias()
102113
.setDescriptorSetLayouts(setLayouts)
103-
.setMultisample(4)
114+
.setMultisample(1)
115+
.setCullMode(vk::CullModeFlagBits::eFront)
104116
.setFrontFace(vk::FrontFace::eClockwise) // Match main geometry pipeline
105117
.addPushContantRange(vk::ShaderStageFlagBits::eVertex, 0, sizeof(glm::mat4));
106118

@@ -115,7 +127,7 @@ void ShadowMapping::createDescriptors()
115127

116128
// Descriptor set bindings: UBO for light's MVP
117129
std::vector<vk::DescriptorSetLayoutBinding> bindings = {
118-
{ 0, vk::DescriptorType::eUniformBuffer, 1, vk::ShaderStageFlagBits::eVertex }
130+
{0, vk::DescriptorType::eUniformBuffer, 1, vk::ShaderStageFlagBits::eVertex}
119131
// Add more if you want—for example for a sampler or shadow map
120132
};
121133

@@ -128,7 +140,7 @@ void ShadowMapping::createDescriptors()
128140
vk::DescriptorBufferInfo uboInfo{};
129141
uboInfo.buffer = m_mvpBuffer[i]->getHandle();
130142
uboInfo.offset = 0;
131-
uboInfo.range = sizeof(glm::mat4);
143+
uboInfo.range = sizeof(SceneUBO);
132144

133145
vk::WriteDescriptorSet writes{};
134146
writes.dstSet = m_descriptors->get(i);
@@ -140,7 +152,76 @@ void ShadowMapping::createDescriptors()
140152

141153
m_descriptors->updateSet({writes});
142154
}
155+
}
156+
157+
void ShadowMapping::recordShadowPass(vk::CommandBuffer cmd,
158+
size_t frameIndex,
159+
const std::function<void(vk::CommandBuffer)>& drawCallback)
160+
{
161+
vk::ClearValue clearDepth;
162+
clearDepth.depthStencil = vk::ClearDepthStencilValue{1.0f, 0};
163+
164+
vk::RenderingAttachmentInfo depthAttachment{};
165+
depthAttachment.imageView = m_shadowMapView;
166+
depthAttachment.imageLayout = vk::ImageLayout::eDepthStencilAttachmentOptimal;
167+
depthAttachment.loadOp = vk::AttachmentLoadOp::eClear;
168+
depthAttachment.storeOp = vk::AttachmentStoreOp::eStore;
169+
depthAttachment.clearValue = clearDepth;
170+
171+
vk::RenderingInfo renderingInfo{};
172+
renderingInfo.renderArea = vk::Rect2D({0, 0}, {m_resolution, m_resolution});
173+
renderingInfo.layerCount = 1;
174+
renderingInfo.pDepthAttachment = &depthAttachment;
175+
176+
cmd.setDepthBias(depthBiasConstant, 0.0f, depthBiasSlope);
177+
178+
cmd.beginRendering(&renderingInfo);
179+
180+
// set viewport/scissor
181+
vk::Viewport viewport = {0.0f, 0.0f, (float)m_resolution, (float)m_resolution, 0.0f, 1.0f};
182+
vk::Rect2D scissor = {vk::Offset2D{0, 0}, vk::Extent2D{m_resolution, m_resolution}};
183+
cmd.setViewport(0, viewport);
184+
cmd.setScissor(0, scissor);
185+
186+
cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, m_depthPassPipeline->get());
187+
188+
auto descriptor = m_descriptors->get(frameIndex);
189+
190+
// bind descriptor set (with light MVP)
191+
cmd.bindDescriptorSets(
192+
vk::PipelineBindPoint::eGraphics, m_depthPassPipeline->getLayout(), 0, 1, &descriptor, 0, nullptr);
143193

194+
// draw the shadow casters
195+
drawCallback(cmd);
196+
197+
cmd.endRendering();
198+
}
199+
200+
vk::ImageView ShadowMapping::shadowMapView() const
201+
{
202+
return m_shadowMapView;
203+
}
204+
205+
vk::Sampler ShadowMapping::shadowMapSampler() const
206+
{
207+
return m_shadowMapSampler;
208+
}
209+
210+
vk::DescriptorSet ShadowMapping::shadowMapDescriptorSet(size_t frameIndex) const
211+
{
212+
return m_descriptors->get(frameIndex);
213+
}
214+
215+
void ShadowMapping::setLightMatrix(const glm::mat4& lightSpaceMatrix, size_t frameIndex)
216+
{
217+
SceneUBO ubo;
218+
ubo.view = glm::mat4(1.0f);
219+
ubo.projection = lightSpaceMatrix;
220+
221+
// map buffer, copy matrix
222+
void* data = m_mvpBuffer[frameIndex]->map();
223+
memcpy(data, &ubo, sizeof(SceneUBO));
224+
m_mvpBuffer[frameIndex]->unmap();
144225
}
145226

146227
} // namespace reactor

src/vulkan/ShadowMapping.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class ShadowMapping
1818
explicit ShadowMapping(VulkanRenderer& renderer, uint32_t resolution = 2048);
1919
~ShadowMapping();
2020

21-
void recordShadowPass(vk::CommandBuffer cmd, size_t frameIndex);
21+
void recordShadowPass(vk::CommandBuffer cmd, size_t frameIndex, const std::function<void(vk::CommandBuffer)>& drawCallback);
2222

2323
vk::ImageView shadowMapView() const;
2424
vk::Sampler shadowMapSampler() const;
@@ -36,6 +36,9 @@ class ShadowMapping
3636
void createPipeline();
3737
void createDescriptors();
3838

39+
const float depthBiasConstant = 1.25f;
40+
const float depthBiasSlope = 1.75f;
41+
3942
VulkanRenderer& m_renderer;
4043
uint32_t m_resolution;
4144

src/vulkan/VulkanRenderer.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,47 @@ void VulkanRenderer::drawFrame()
359359
drawGeometry(cmd);
360360
endDynamicRendering(cmd);
361361

362+
// Shadow pass
363+
// --- Prepare for Shadow Pass ---
364+
// 1. Create the orthographic projection matrix for the directional light.
365+
// This defines a "box" in space that will cast shadows.
366+
// You can adjust these values to fit your scene's size.
367+
const float orthoSize = 10.0f;
368+
const float nearPlane = 0.1f;
369+
const float farPlane = 100.0f;
370+
glm::mat4 lightProjection = glm::ortho(-orthoSize, orthoSize, -orthoSize, orthoSize, nearPlane, farPlane);
371+
372+
// 2. Create the view matrix from the light's perspective.
373+
// This uses the light's direction, which is already updated in your m_light UBO.
374+
// We place the light "camera" at a position based on its direction and make it look at the scene origin.
375+
glm::vec3 lightPosition = glm::vec3(15.0f, 15.0f, 5.0f);
376+
glm::mat4 lightView = glm::lookAt(
377+
lightPosition, // Position of the light in world space
378+
glm::vec3(0.0f, 0.0f, 0.0f), // The point the light is looking at (scene origin)
379+
glm::vec3(0.0f, 1.0f, 0.0f) // Up vector
380+
);
381+
382+
// 3. Combine the matrices to create the final light-space matrix.
383+
// Note: Vulkan's clip space has an inverted Y-axis. We need to add a correction.
384+
glm::mat4 clipCorrection = {
385+
1.0f, 0.0f, 0.0f, 0.0f,
386+
0.0f,-1.0f, 0.0f, 0.0f,
387+
0.0f, 0.0f, 0.5f, 0.5f,
388+
0.0f, 0.0f, 0.0f, 1.0f
389+
};
390+
391+
glm::mat4 lightSpaceMatrix = clipCorrection * lightProjection * lightView;
392+
393+
// 4. Set the matrix for the shadow mapping pass.
394+
m_shadowMapping->setLightMatrix(lightSpaceMatrix, frameIdx);
395+
396+
//m_shadowMapping->setLightMatrix(lightMVP, frameIdx);
397+
auto drawFunc = [this](vk::CommandBuffer cmd) {
398+
this->drawGeometry(cmd);
399+
};
400+
401+
m_shadowMapping->recordShadowPass(cmd, frameIdx, drawFunc);
402+
362403
// --- 1. Geometry Pass ---
363404
// Transition the MSAA image so we can render the main scene into it.
364405
// Its layout was likely UNDEFINED (on first use) or TRANSFER_SRC (from previous frame's resolve).

0 commit comments

Comments
 (0)