From 389c979f2fb908bad6096a39588c22ff03a1964e Mon Sep 17 00:00:00 2001 From: bailehuni Date: Tue, 26 Mar 2024 11:18:50 +0000 Subject: [PATCH] Get shadow mapping mostly working --- include/gfx.h | 1 + include/gfx_device.h | 4 + include/renderer.h | 3 + res/engine/shaders/fancy.frag | 18 +-- res/engine/shaders/fancy.vert | 3 +- res/engine/shaders/shadow.frag | 5 + res/engine/shaders/shadow.vert | 17 +++ src/gfx_device_vulkan.cpp | 242 +++++++++++++++++++++++++++++++-- src/renderer.cpp | 98 +++++++++---- 9 files changed, 345 insertions(+), 46 deletions(-) create mode 100644 res/engine/shaders/shadow.frag create mode 100644 res/engine/shaders/shadow.vert diff --git a/include/gfx.h b/include/gfx.h index da4ba3e..c395da7 100644 --- a/include/gfx.h +++ b/include/gfx.h @@ -123,6 +123,7 @@ struct PipelineInfo { bool alpha_blending; bool write_z; bool line_primitives; // false for triangles, true for lines + bool depth_attachment_only; // false 99% of the time std::vector descriptor_set_layouts; }; diff --git a/include/gfx_device.h b/include/gfx_device.h index 4fc41d6..f14d208 100644 --- a/include/gfx_device.h +++ b/include/gfx_device.h @@ -31,6 +31,10 @@ class GFXDevice { - draw_buffer is invalid after this function has been called. */ void FinishRender(gfx::DrawBuffer* draw_buffer); + gfx::Image* CreateShadowmapImage(); + gfx::DrawBuffer* BeginShadowmapRender(gfx::Image* image); + void FinishShadowmapRender(gfx::DrawBuffer* draw_buffer, gfx::Image* image); + /* - draw_buffer MUST be a valid pointer returned by BeginRender() - pipeline MUST be a valid pointer returned by CreatePipeline() */ void CmdBindPipeline(gfx::DrawBuffer* draw_buffer, diff --git a/include/renderer.h b/include/renderer.h index 07e2a38..eda62d4 100644 --- a/include/renderer.h +++ b/include/renderer.h @@ -112,6 +112,9 @@ class Renderer : private ApplicationComponent { gfx::Image* shadow_map = nullptr; const gfx::Sampler* shadow_map_sampler = nullptr; + const gfx::Pipeline* shadow_pipeline = nullptr; + + bool rendering_started = false; void DrawRenderList(gfx::DrawBuffer* draw_buffer, const RenderList& render_list); }; diff --git a/res/engine/shaders/fancy.frag b/res/engine/shaders/fancy.frag index 0510c20..92a1270 100644 --- a/res/engine/shaders/fancy.frag +++ b/res/engine/shaders/fancy.frag @@ -51,7 +51,7 @@ void main() { const vec3 N = GetNormal(); const vec3 V = normalize(fragViewPosTangentSpace - fragPosTangentSpace); - const vec3 L = normalize(fragLightPosTangentSpace); + const vec3 L = normalize(fragLightPosTangentSpace - fragPosTangentSpace); //const vec3 L = normalize(vec3(5.0, 0.0, 3.0)); const vec3 H = normalize(V + L); @@ -80,16 +80,18 @@ void main() { vec3 lighting = brdf * light_colour * L_dot_N; - vec3 ambient_light = vec3(0.09082, 0.13281, 0.18164); - lighting += mix(ambient_light, texture(globalSetSkybox, R).rgb, metallic) * ao * diffuse_brdf; // this is NOT physically-based, it just looks cool - - // perform perspective divide - vec3 projCoords = fragPosLightSpace.xyz; + // find if fragment is in shadow + vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; projCoords.x = projCoords.x * 0.5 + 0.5; projCoords.y = projCoords.y * 0.5 + 0.5; float closestDepth = texture(globalSetShadowmap, projCoords.xy).r; float currentDepth = projCoords.z; - float shadow = currentDepth > closestDepth ? 1.0 : 0.0; + float shadow = currentDepth > closestDepth ? 0.0 : 1.0; + lighting *= shadow; + + vec3 ambient_light = vec3(0.09082, 0.13281, 0.18164); + lighting += mix(ambient_light, texture(globalSetSkybox, R).rgb, metallic) * ao * diffuse_brdf; // this is NOT physically-based, it just looks cool + outColor = vec4(min(emission + lighting, 1.0), 1.0); - //outColor = vec4(shadow, 0.0, 0.0, 1.0); + //outColor = vec4(vec3(shadow), 1.0); } diff --git a/res/engine/shaders/fancy.vert b/res/engine/shaders/fancy.vert index 5fadbc5..fcaa88e 100644 --- a/res/engine/shaders/fancy.vert +++ b/res/engine/shaders/fancy.vert @@ -39,7 +39,8 @@ void main() { fragUV = inUV; fragPosTangentSpace = worldToTangentSpace * vec3(worldPosition); fragViewPosTangentSpace = worldToTangentSpace * vec3(inverse(frameSetUniformBuffer.view) * vec4(0.0, 0.0, 0.0, 1.0)); - fragLightPosTangentSpace = worldToTangentSpace * vec3(-0.4278,0.7923,0.43502); + //fragLightPosTangentSpace = worldToTangentSpace * vec3(-0.4278,0.7923,0.43502); + fragLightPosTangentSpace = worldToTangentSpace * vec3(10.0, 0.0, 10.0); fragNormWorldSpace = N; fragViewPosWorldSpace = vec3(inverse(frameSetUniformBuffer.view) * vec4(0.0, 0.0, 0.0, 1.0)); diff --git a/res/engine/shaders/shadow.frag b/res/engine/shaders/shadow.frag new file mode 100644 index 0000000..a5e029e --- /dev/null +++ b/res/engine/shaders/shadow.frag @@ -0,0 +1,5 @@ +#version 450 + +void main() { + // empty fragment shader +} diff --git a/res/engine/shaders/shadow.vert b/res/engine/shaders/shadow.vert new file mode 100644 index 0000000..ef94832 --- /dev/null +++ b/res/engine/shaders/shadow.vert @@ -0,0 +1,17 @@ +#version 450 + +layout(set = 0, binding = 0) uniform GlobalSetUniformBuffer { + mat4 proj; + mat4 lightSpaceMatrix; +} globalSetUniformBuffer; + +layout( push_constant ) uniform Constants { + mat4 model; +} constants; + +layout(location = 0) in vec3 inPosition; + +void main() { + gl_Position = globalSetUniformBuffer.lightSpaceMatrix * constants.model * vec4(inPosition, 1.0); + //gl_Position.y *= -1.0; +} diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index cd082c9..0a2010d 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -72,6 +72,8 @@ static constexpr uint32_t FRAMES_IN_FLIGHT = 2; // This improved FPS by 5x static constexpr size_t PUSH_CONSTANT_MAX_SIZE = 128; // bytes static constexpr VkIndexType INDEX_TYPE = VK_INDEX_TYPE_UINT32; +static constexpr int kShadowmapSize = 2048; + // structures and enums struct FrameData { @@ -677,7 +679,7 @@ gfx::DrawBuffer* GFXDevice::BeginRender() // ownership transfer isn't needed since the data isn't accessed it's just overwritten { - // copy stagings buffers to GPU buffer + // copy staging buffers to GPU buffers for (gfx::UniformBuffer* uniformBuffer : pimpl->write_queues[currentFrameIndex].uniform_buffer_writes) { VkBufferCopy copyRegion{}; copyRegion.srcOffset = 0; @@ -889,7 +891,7 @@ gfx::DrawBuffer* GFXDevice::BeginRender() pimpl->write_queues[currentFrameIndex].uniform_buffer_writes.clear(); // hand command buffer over to caller - gfx::DrawBuffer* drawBuffer = new gfx::DrawBuffer; + gfx::DrawBuffer* drawBuffer = new gfx::DrawBuffer; // heap allocation every frame but it's only 72 bytes drawBuffer->frameData = frameData; drawBuffer->currentFrameIndex = currentFrameIndex; drawBuffer->imageIndex = swapchainImageIndex; @@ -964,7 +966,7 @@ void GFXDevice::FinishRender(gfx::DrawBuffer* drawBuffer) std::vector waitSemaphores{}; std::vector waitDstStageMasks{}; - waitSemaphores.push_back(drawBuffer->frameData.presentSemaphore); // wait for image from 2nd last frame to be presented so it can be rendered to again + waitSemaphores.push_back(drawBuffer->frameData.presentSemaphore); // wait for image from 2nd last frame to be presented so it can be rendered to again waitDstStageMasks.push_back(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT); waitSemaphores.push_back(drawBuffer->frameData.transferSemaphore); // wait for uniform buffer copies to complete waitDstStageMasks.push_back(VK_PIPELINE_STAGE_VERTEX_SHADER_BIT); @@ -1006,6 +1008,222 @@ void GFXDevice::FinishRender(gfx::DrawBuffer* drawBuffer) delete drawBuffer; } +gfx::Image* GFXDevice::CreateShadowmapImage() +{ + if (pimpl->FRAMECOUNT != 0) abort(); + + gfx::Image* out = new gfx::Image{}; + + VkImageCreateInfo imageInfo{}; + imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + imageInfo.flags = 0; + imageInfo.imageType = VK_IMAGE_TYPE_2D; + imageInfo.format = pimpl->swapchain.depthStencilFormat; + imageInfo.extent.width = kShadowmapSize; + imageInfo.extent.height = kShadowmapSize; + imageInfo.extent.depth = 1; + imageInfo.mipLevels = 1; + imageInfo.arrayLayers = 1; + imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + imageInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + + VmaAllocationCreateInfo allocCreateInfo{}; + allocCreateInfo.flags = 0; + allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; + allocCreateInfo.priority = 0.5f; + + VKCHECK(vmaCreateImage(pimpl->allocator, &imageInfo, &allocCreateInfo, &out->image, &out->allocation, nullptr)); + + VkImageViewCreateInfo viewInfo{}; + viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + viewInfo.image = out->image; + viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + viewInfo.format = pimpl->swapchain.depthStencilFormat; + viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + viewInfo.subresourceRange.baseMipLevel = 0; + viewInfo.subresourceRange.levelCount = 1; + viewInfo.subresourceRange.baseArrayLayer = 0; + viewInfo.subresourceRange.layerCount = 1; + + VKCHECK(vkCreateImageView(pimpl->device.device, &viewInfo, nullptr, &out->view)); + + return out; +} + +gfx::DrawBuffer* GFXDevice::BeginShadowmapRender(gfx::Image* image) +{ + assert(image != nullptr); + if (pimpl->FRAMECOUNT != 0) throw std::runtime_error("Can only create shadowmap before proper rendering begins."); + + VkResult res; + + /* record command buffer */ + res = vkResetCommandPool(pimpl->device.device, pimpl->graphicsCommandPool, 0); + VKCHECK(res); + + VkCommandBufferBeginInfo beginInfo{ + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .pNext = nullptr, + .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, + .pInheritanceInfo = nullptr // ignored + }; + res = vkBeginCommandBuffer(pimpl->frameData[0].drawBuf, &beginInfo); + VKCHECK(res); + + { + // make the depth image an attachment layout + VkImageMemoryBarrier2 barrier{}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2; + barrier.pNext = nullptr; + barrier.srcStageMask = VK_PIPELINE_STAGE_2_NONE; + barrier.dstStageMask = VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT; + barrier.srcAccessMask = VK_ACCESS_2_NONE; + barrier.dstAccessMask = VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + barrier.newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + barrier.srcQueueFamilyIndex = pimpl->device.queues.presentAndDrawQueueFamily; + barrier.dstQueueFamilyIndex = pimpl->device.queues.presentAndDrawQueueFamily; + barrier.image = image->image; + VkImageSubresourceRange range{}; + range.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; + range.baseMipLevel = 0; + range.levelCount = 1; + range.baseArrayLayer = 0; + range.layerCount = 1; + barrier.subresourceRange = range; + + VkDependencyInfo imageDependencyInfo{}; + imageDependencyInfo.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO; + imageDependencyInfo.imageMemoryBarrierCount = 1; + imageDependencyInfo.pImageMemoryBarriers = &barrier; + vkCmdPipelineBarrier2(pimpl->frameData[0].drawBuf, &imageDependencyInfo); + + VkClearValue clearValue{}; + clearValue.depthStencil.depth = 1.0f; + + VkRenderingAttachmentInfo depthAttachment{}; + depthAttachment.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO; + depthAttachment.pNext = nullptr; + depthAttachment.imageView = image->view; + depthAttachment.imageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + depthAttachment.resolveMode = VK_RESOLVE_MODE_NONE; + depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + depthAttachment.clearValue = clearValue; + + VkRenderingInfo renderingInfo{}; + renderingInfo.sType = VK_STRUCTURE_TYPE_RENDERING_INFO; + renderingInfo.pNext = nullptr; + renderingInfo.flags = 0; + renderingInfo.renderArea = VkRect2D{VkOffset2D{0, 0}, VkExtent2D{kShadowmapSize, kShadowmapSize}}; + renderingInfo.layerCount = 1; + renderingInfo.viewMask = 0; + renderingInfo.colorAttachmentCount = 0; + renderingInfo.pColorAttachments = nullptr; // no color attachment for this render pass + renderingInfo.pDepthAttachment = &depthAttachment; + renderingInfo.pStencilAttachment = nullptr; + vkCmdBeginRendering(pimpl->frameData[0].drawBuf, &renderingInfo); + + VkViewport viewport{}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = (float)kShadowmapSize; + viewport.height = (float)kShadowmapSize; + + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + vkCmdSetViewport(pimpl->frameData[0].drawBuf, 0, 1, &viewport); + + VkRect2D scissor{}; + scissor.offset = {0, 0}; + scissor.extent = { kShadowmapSize, kShadowmapSize }; + vkCmdSetScissor(pimpl->frameData[0].drawBuf, 0, 1, &scissor); + + // Depth bias (and slope) are used to avoid shadowing artifacts + // Constant depth bias factor (always applied) + constexpr float depthBiasConstant = 1.25f; + // Slope depth bias factor, applied depending on polygon's slope + constexpr float depthBiasSlope = 1.75f; + // Set depth bias (aka "Polygon offset") + // Required to avoid shadow mapping artifacts + vkCmdSetDepthBias( + pimpl->frameData[0].drawBuf, + depthBiasConstant, + 0.0f, + depthBiasSlope); + + } + + // hand command buffer over to caller + gfx::DrawBuffer* drawBuffer = new gfx::DrawBuffer; // heap allocation every frame but it's only 72 bytes + drawBuffer->frameData = pimpl->frameData[0]; + drawBuffer->currentFrameIndex = 0; + drawBuffer->imageIndex = std::numeric_limits::max(); // meaningless here + return drawBuffer; +} + +void GFXDevice::FinishShadowmapRender(gfx::DrawBuffer* draw_buffer, gfx::Image* image) +{ + assert(draw_buffer != nullptr); + + VkResult res; + + vkCmdEndRendering(draw_buffer->frameData.drawBuf); + + // make the depth image readable by a sampler + VkImageMemoryBarrier2 barrier{}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2; + barrier.pNext = nullptr; + barrier.srcStageMask = VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT; + barrier.dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT; + barrier.srcAccessMask = VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_2_SHADER_SAMPLED_READ_BIT; + barrier.oldLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + barrier.srcQueueFamilyIndex = pimpl->device.queues.presentAndDrawQueueFamily; + barrier.dstQueueFamilyIndex = pimpl->device.queues.presentAndDrawQueueFamily; + barrier.image = image->image; + VkImageSubresourceRange range{}; + range.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; + range.baseMipLevel = 0; + range.levelCount = 1; + range.baseArrayLayer = 0; + range.layerCount = 1; + barrier.subresourceRange = range; + + VkDependencyInfo imageDependencyInfo{}; + imageDependencyInfo.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO; + imageDependencyInfo.imageMemoryBarrierCount = 1; + imageDependencyInfo.pImageMemoryBarriers = &barrier; + vkCmdPipelineBarrier2(draw_buffer->frameData.drawBuf, &imageDependencyInfo); + + res = vkEndCommandBuffer(draw_buffer->frameData.drawBuf); + VKCHECK(res); + + // SUBMIT + + VkSubmitInfo submitInfo{ + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .pNext = nullptr, + .waitSemaphoreCount = 0, + .pWaitSemaphores = nullptr, + .pWaitDstStageMask = nullptr, + .commandBufferCount = 1, + .pCommandBuffers = &draw_buffer->frameData.drawBuf, + .signalSemaphoreCount = 0, + .pSignalSemaphores = nullptr, + }; + res = vkQueueSubmit(pimpl->device.queues.drawQueues[0], 1, &submitInfo, VK_NULL_HANDLE); + VKCHECK(res); + + vkQueueWaitIdle(pimpl->device.queues.drawQueues[0]); + + delete draw_buffer; +} + void GFXDevice::CmdBindPipeline(gfx::DrawBuffer* drawBuffer, const gfx::Pipeline* pipeline) { assert(drawBuffer != nullptr); @@ -1124,6 +1342,7 @@ gfx::Pipeline* GFXDevice::CreatePipeline(const gfx::PipelineInfo& info) inputAssembly.topology = info.line_primitives ? VK_PRIMITIVE_TOPOLOGY_LINE_LIST : VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; inputAssembly.primitiveRestartEnable = VK_FALSE; +#if 0 VkViewport viewport{}; if (flip_viewport) { viewport.x = 0.0f; @@ -1143,6 +1362,7 @@ gfx::Pipeline* GFXDevice::CreatePipeline(const gfx::PipelineInfo& info) VkRect2D scissor{}; scissor.offset = {0, 0}; scissor.extent = pimpl->swapchain.extent; +#endif // Dynamic states removes the need to re-create pipelines whenever the window // size changes @@ -1156,9 +1376,9 @@ gfx::Pipeline* GFXDevice::CreatePipeline(const gfx::PipelineInfo& info) VkPipelineViewportStateCreateInfo viewportState{}; viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; viewportState.viewportCount = 1; - viewportState.pViewports = &viewport; + viewportState.pViewports = nullptr; viewportState.scissorCount = 1; - viewportState.pScissors = &scissor; + viewportState.pScissors = nullptr; VkPipelineRasterizationStateCreateInfo rasterizer{}; rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; @@ -1246,7 +1466,7 @@ gfx::Pipeline* GFXDevice::CreatePipeline(const gfx::PipelineInfo& info) createInfo.pStages = shaderStages; createInfo.pVertexInputState = &vertexInputInfo; createInfo.pInputAssemblyState = &inputAssembly; - createInfo.pViewportState = &viewportState; // TODO: maybe this isn't needed? + createInfo.pViewportState = &viewportState; createInfo.pRasterizationState = &rasterizer; createInfo.pMultisampleState = &multisampling; createInfo.pDepthStencilState = &depthStencil; @@ -1260,8 +1480,14 @@ gfx::Pipeline* GFXDevice::CreatePipeline(const gfx::PipelineInfo& info) VkPipelineRenderingCreateInfo renderingInfo{}; renderingInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO; - renderingInfo.colorAttachmentCount = 1; - renderingInfo.pColorAttachmentFormats = &pimpl->swapchain.surfaceFormat.format; + if (info.depth_attachment_only) { + renderingInfo.colorAttachmentCount = 0; + renderingInfo.pColorAttachmentFormats = nullptr; + } + else { + renderingInfo.colorAttachmentCount = 1; + renderingInfo.pColorAttachmentFormats = &pimpl->swapchain.surfaceFormat.format; + } renderingInfo.depthAttachmentFormat = pimpl->swapchain.depthStencilFormat; createInfo.pNext = &renderingInfo; diff --git a/src/renderer.cpp b/src/renderer.cpp index 28d8401..87d99c0 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -32,10 +32,12 @@ Renderer::Renderer(Application& app, gfx::GraphicsSettings settings) : Applicati } global_uniform.layout = device_->CreateDescriptorSetLayout(globalSetBindings); global_uniform.set = device_->AllocateDescriptorSet(global_uniform.layout); - global_uniform.uniform_buffer_data.data.proj = glm::mat4{1.0f}; - const glm::vec3 light_location{ -0.4278, 0.7923, 0.43502 }; - const glm::mat4 light_proj = glm::orthoRH_ZO(-32.0f, 32.0f, -32.0f, 32.0f, -100.0f, 100.0f); - const glm::mat4 light_view = glm::lookAtRH(light_location, glm::vec3{ 0.0f, 0.0f, 0.0f }, glm::vec3{ 0.0f, 0.0f, 1.0f }); + //const glm::vec3 light_location = glm::vec3{-0.4278, 0.7923, 0.43502} * 10.0f; + const glm::vec3 light_location = glm::vec3{ 10.0f, 0.0f, 10.0f }; + //const glm::mat4 light_proj = glm::orthoRH_ZO(-10.0f, 10.0f, -10.0f, 10.0f, 1.0f, 50.0f); + const glm::mat4 light_proj = glm::perspectiveFovRH_ZO(glm::radians(90.0f), 1.0f, 1.0f, 5.0f, 50.0f); + const glm::mat4 light_view = glm::lookAtRH(light_location, glm::vec3{0.0f, 0.0f, 0.0f}, glm::vec3{0.0f, 0.0f, 1.0f}); + global_uniform.uniform_buffer_data.data.proj = light_proj; global_uniform.uniform_buffer_data.data.lightSpaceMatrix = light_proj * light_view; global_uniform.uniform_buffer = device_->CreateUniformBuffer(sizeof(global_uniform.uniform_buffer_data), &global_uniform.uniform_buffer_data); device_->UpdateDescriptorUniformBuffer(global_uniform.set, 0, global_uniform.uniform_buffer, 0, sizeof(global_uniform.uniform_buffer_data)); @@ -50,7 +52,7 @@ Renderer::Renderer(Application& app, gfx::GraphicsSettings settings) : Applicati } frame_uniform.layout = device_->CreateDescriptorSetLayout(frameSetBindings); frame_uniform.set = device_->AllocateDescriptorSet(frame_uniform.layout); - frame_uniform.uniform_buffer_data.data = glm::mat4{1.0f}; + frame_uniform.uniform_buffer_data.data = light_view; frame_uniform.uniform_buffer = device_->CreateUniformBuffer(sizeof(frame_uniform.uniform_buffer_data), &frame_uniform.uniform_buffer_data); device_->UpdateDescriptorUniformBuffer(frame_uniform.set, 0, frame_uniform.uniform_buffer, 0, sizeof(frame_uniform.uniform_buffer_data)); @@ -188,28 +190,26 @@ Renderer::Renderer(Application& app, gfx::GraphicsSettings settings) : Applicati skybox_buffer = device_->CreateBuffer(gfx::BufferType::kVertex, v.size() * sizeof(glm::vec3), v.data()); } - - // shadow mapping... - { - int w{}, h{}; - auto shadowmap_image = util::ReadImageFile(GetResourcePath("textures/shadow_map.png"), w, h); - shadow_map = device_->CreateImage(w, h, gfx::ImageFormat::kLinear, shadowmap_image->data()); - gfx::SamplerInfo sampler_info{}; - sampler_info.magnify = gfx::Filter::kLinear; - sampler_info.minify = gfx::Filter::kLinear; - sampler_info.mipmap = gfx::Filter::kLinear; // trilinear is apparently good for shadow maps - sampler_info.wrap_u = gfx::WrapMode::kClampToEdge; - sampler_info.wrap_v = gfx::WrapMode::kClampToEdge; - sampler_info.wrap_w = gfx::WrapMode::kClampToEdge; - sampler_info.anisotropic_filtering = false; // Copilot says not to use aniso for shadow maps - shadow_map_sampler = device_->CreateSampler(sampler_info); - - device_->UpdateDescriptorCombinedImageSampler(global_uniform.set, 2, shadow_map, shadow_map_sampler); - } + gfx::VertexFormat shadowVertexFormat{}; + shadowVertexFormat.stride = sizeof(float) * 12; // using the full meshes so a lot of data is skipped + shadowVertexFormat.attribute_descriptions.emplace_back(0, gfx::VertexAttribFormat::kFloat3, 0); // position + gfx::PipelineInfo shadowPipelineInfo{}; + shadowPipelineInfo.vert_shader_path = GetResourcePath("engine/shaders/shadow.vert"); + shadowPipelineInfo.frag_shader_path = GetResourcePath("engine/shaders/shadow.frag"); + shadowPipelineInfo.vertex_format = shadowVertexFormat; + shadowPipelineInfo.face_cull_mode = gfx::CullMode::kCullBack; + shadowPipelineInfo.alpha_blending = false; + shadowPipelineInfo.write_z = true; + shadowPipelineInfo.line_primitives = false; + shadowPipelineInfo.depth_attachment_only = true; + shadowPipelineInfo.descriptor_set_layouts.emplace_back(GetGlobalSetLayout()); + shadow_pipeline = device_->CreatePipeline(shadowPipelineInfo); }; Renderer::~Renderer() { + device_->DestroyPipeline(shadow_pipeline); + device_->DestroySampler(shadow_map_sampler); device_->DestroyImage(shadow_map); @@ -248,16 +248,56 @@ void Renderer::PreRender(bool window_is_resized, glm::mat4 camera_transform) const glm::mat4 view_matrix = glm::inverse(camera_transform); frame_uniform.uniform_buffer_data.data = view_matrix; device_->WriteUniformBuffer(frame_uniform.uniform_buffer, 0, sizeof(frame_uniform.uniform_buffer_data), &frame_uniform.uniform_buffer_data); - - // override with light matrix - //frame_uniform.uniform_buffer_data.data = glm::mat4{1.0f}; - //device_->WriteUniformBuffer(frame_uniform.uniform_buffer, 0, sizeof(frame_uniform.uniform_buffer_data), &frame_uniform.uniform_buffer_data); - //global_uniform.uniform_buffer_data.data.proj = global_uniform.uniform_buffer_data.data.lightSpaceMatrix; - //device_->WriteUniformBuffer(global_uniform.uniform_buffer, 0, sizeof(global_uniform.uniform_buffer_data), &global_uniform.uniform_buffer_data); } void Renderer::Render(const RenderList* static_list, const RenderList* dynamic_list, const std::vector& debug_lines) { + + if (rendering_started == false) { + // shadow mapping... + + shadow_map = device_->CreateShadowmapImage(); + + // render to shadow map + gfx::DrawBuffer* shadow_draw = device_->BeginShadowmapRender(shadow_map); + device_->CmdBindPipeline(shadow_draw, shadow_pipeline); + device_->CmdBindDescriptorSet(shadow_draw, shadow_pipeline, global_uniform.set, 0); // only need light space matrix + if (static_list) { + if (!static_list->empty()) { + for (const auto& entry : *static_list) { + device_->CmdPushConstants(shadow_draw, shadow_pipeline, 0, sizeof(entry.model_matrix), &entry.model_matrix); + device_->CmdBindVertexBuffer(shadow_draw, 0, entry.vertex_buffer); + device_->CmdBindIndexBuffer(shadow_draw, entry.index_buffer); + device_->CmdDrawIndexed(shadow_draw, entry.index_count, 1, 0, 0, 0); + } + } + } + if (dynamic_list) { + if (!dynamic_list->empty()) { + for (const auto& entry : *dynamic_list) { + device_->CmdPushConstants(shadow_draw, shadow_pipeline, 0, sizeof(entry.model_matrix), &entry.model_matrix); + device_->CmdBindVertexBuffer(shadow_draw, 0, entry.vertex_buffer); + device_->CmdBindIndexBuffer(shadow_draw, entry.index_buffer); + device_->CmdDrawIndexed(shadow_draw, entry.index_count, 1, 0, 0, 0); + } + } + } + device_->FinishShadowmapRender(shadow_draw, shadow_map); + + gfx::SamplerInfo sampler_info{}; + sampler_info.magnify = gfx::Filter::kLinear; + sampler_info.minify = gfx::Filter::kLinear; + sampler_info.mipmap = gfx::Filter::kLinear; // trilinear is apparently good for shadow maps + sampler_info.wrap_u = gfx::WrapMode::kClampToEdge; + sampler_info.wrap_v = gfx::WrapMode::kClampToEdge; + sampler_info.wrap_w = gfx::WrapMode::kClampToEdge; + sampler_info.anisotropic_filtering = false; // Copilot says not to use aniso for shadow maps + shadow_map_sampler = device_->CreateSampler(sampler_info); + + device_->UpdateDescriptorCombinedImageSampler(global_uniform.set, 2, shadow_map, shadow_map_sampler); + } + rendering_started = true; + last_bound_pipeline_ = nullptr; gfx::DrawBuffer* draw_buffer = device_->BeginRender();