Run shadow render pass and compute light position in vertex shaders

This commit is contained in:
Camden Dixie O'Brien committed 2025-10-04 21:27:46 +01:00
1 parent 73340b3a39
commit 633a922e42
6 files changed
+121 -23

No files matched your search

+95 -10
View File
@@ -21,6 +21,11 @@
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define CLAMP(x, min, max) MAX((min), MIN(x, (max)))
typedef struct {
mat4_t vp;
mat4_t light_vp;
} push_t;
static const char *layers[] = { "VK_LAYER_KHRONOS_validation" };
static const char *extensions[] = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
@@ -547,7 +552,7 @@ void gpu_init(gpu_ctx_t *ctx, GLFWwindow *window, const char *name)
const VkPushConstantRange push_constant_range = {
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
.offset = 0,
.size = sizeof(mat4_t),
.size = sizeof(push_t),
};
// Load vertex and fragment shader modules
@@ -1083,7 +1088,7 @@ void gpu_model_cleanup(const gpu_ctx_t *ctx, const gpu_model_t *model)
}
void gpu_render(
const gpu_ctx_t *ctx, mat4_t vp, size_t model_count,
const gpu_ctx_t *ctx, mat4_t vp, mat4_t light_vp, size_t model_count,
const gpu_model_t *models, const mat4_t **bone_matrices,
const mat4_t *model_transforms)
{
@@ -1100,6 +1105,14 @@ void gpu_render(
&curr_img);
assert(result == VK_SUCCESS);
// Update models' transforms and bones
for (unsigned i = 0; i < model_count; ++i) {
models[i].transform_ptr[curr_img] = model_transforms[i];
memcpy(
models[i].bone_ptr + (models[i].bone_count * curr_img),
bone_matrices[i], models[i].bone_count * sizeof(mat4_t));
}
// Reset command buffer, then begin writing
vkResetCommandBuffer(ctx->cmd_buf, 0);
const VkCommandBufferBeginInfo cmd_begin = {
@@ -1108,6 +1121,61 @@ void gpu_render(
result = vkBeginCommandBuffer(ctx->cmd_buf, &cmd_begin);
assert(result == VK_SUCCESS);
// Run shadow pass
VkRenderPassBeginInfo shadow_pass_begin = {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
.renderPass = ctx->shadow_pass,
.framebuffer = ctx->shadow_framebuf,
.renderArea.extent = {
.width = SHADOW_IMG_SIZE,
.height = SHADOW_IMG_SIZE,
},
.clearValueCount = 1,
.pClearValues = &(VkClearValue){ .depthStencil = { 1.0, 0.0 } },
};
vkCmdBeginRenderPass(
ctx->cmd_buf, &shadow_pass_begin, VK_SUBPASS_CONTENTS_INLINE);
vkCmdBindPipeline(
ctx->cmd_buf, VK_PIPELINE_BIND_POINT_GRAPHICS, ctx->shadow_pipeline);
vkCmdPushConstants(
ctx->cmd_buf, ctx->model_pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT,
0, sizeof(push_t), &(push_t) { .vp = light_vp });
for (unsigned i = 0; i < model_count; ++i) {
vkCmdBindVertexBuffers(
ctx->cmd_buf, 0, 1, &models[i].vertex_buf,
&(VkDeviceSize) { 0 });
vkCmdBindIndexBuffer(
ctx->cmd_buf, models[i].index_buf, 0, VK_INDEX_TYPE_UINT16);
vkCmdBindDescriptorSets(
ctx->cmd_buf, VK_PIPELINE_BIND_POINT_GRAPHICS,
ctx->model_pipeline_layout, 0, 1, models[i].desc_sets + curr_img,
0, nullptr);
vkCmdDrawIndexed(ctx->cmd_buf, models[i].index_count, 1, 0, 0, 0);
}
vkCmdEndRenderPass(ctx->cmd_buf);
// Wait until shadow map is fully written and transition to shader
// read-only layout.
const VkImageMemoryBarrier shadows_ready = {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
.oldLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
.image = ctx->shadow_img,
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT,
.levelCount = 1,
.layerCount = 1,
},
};
vkCmdPipelineBarrier(
ctx->cmd_buf, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1,
&shadows_ready);
VkClearValue clear_values[] = {
[0] = { .color = { { 0.0, 0.0, 0.0, 1.0 } } },
[1] = { .depthStencil = { 1.0, 0.0 } },
@@ -1123,12 +1191,14 @@ void gpu_render(
vkCmdBeginRenderPass(
ctx->cmd_buf, &render_pass_begin, VK_SUBPASS_CONTENTS_INLINE);
const push_t push = { .vp = vp, .light_vp = light_vp };
// Draw floor
vkCmdBindPipeline(
ctx->cmd_buf, VK_PIPELINE_BIND_POINT_GRAPHICS, ctx->floor_pipeline);
vkCmdPushConstants(
ctx->cmd_buf, ctx->model_pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT,
0, sizeof(mat4_t), &vp);
0, sizeof(push_t), &push);
vkCmdDraw(ctx->cmd_buf, 4, 1, 0, 0);
// Draw models
@@ -1136,13 +1206,8 @@ void gpu_render(
ctx->cmd_buf, VK_PIPELINE_BIND_POINT_GRAPHICS, ctx->model_pipeline);
vkCmdPushConstants(
ctx->cmd_buf, ctx->model_pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT,
0, sizeof(mat4_t), &vp);
0, sizeof(push_t), &push);
for (unsigned i = 0; i < model_count; ++i) {
models[i].transform_ptr[curr_img] = model_transforms[i];
memcpy(
models[i].bone_ptr + (models[i].bone_count * curr_img),
bone_matrices[i], models[i].bone_count * sizeof(mat4_t));
vkCmdBindVertexBuffers(
ctx->cmd_buf, 0, 1, &models[i].vertex_buf,
&(VkDeviceSize) { 0 });
@@ -1157,8 +1222,28 @@ void gpu_render(
vkCmdDrawIndexed(ctx->cmd_buf, models[i].index_count, 1, 0, 0, 0);
}
// Finish render pass and end writing to command buffer
vkCmdEndRenderPass(ctx->cmd_buf);
// Wait until render pass is finished reading shadow map and transition
// back to depth stencil layout
const VkImageMemoryBarrier shadows_done = {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = VK_ACCESS_SHADER_READ_BIT,
.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
.image = ctx->shadow_img,
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT,
.levelCount = 1,
.layerCount = 1,
},
};
vkCmdPipelineBarrier(
ctx->cmd_buf, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, 0, 0, nullptr, 0,
nullptr, 1, &shadows_done);
result = vkEndCommandBuffer(ctx->cmd_buf);
assert(result == VK_SUCCESS);