Get shadow mapping mostly working

This commit is contained in:
bailehuni 2024-03-26 11:18:50 +00:00
parent dcd71d3a14
commit 389c979f2f
9 changed files with 345 additions and 46 deletions

View File

@ -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<const DescriptorSetLayout*> descriptor_set_layouts;
};

View File

@ -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,

View File

@ -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);
};

View File

@ -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);
}

View File

@ -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));

View File

@ -0,0 +1,5 @@
#version 450
void main() {
// empty fragment shader
}

View File

@ -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;
}

View File

@ -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<VkSemaphore> waitSemaphores{};
std::vector<VkPipelineStageFlags> 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<uint32_t>::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;

View File

@ -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<Line>& 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();