This commit is contained in:
xsl
2025-09-04 10:54:47 +08:00
commit 6bc8f61b18
1808 changed files with 208268 additions and 0 deletions
+741
View File
@@ -0,0 +1,741 @@
/* Copyright (c) 2022-2025, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* High dynamic range rendering, using vulkan.hpp
*/
#include "hpp_hdr.h"
HPPHDR::HPPHDR()
{
title = "HPP High dynamic range rendering";
}
HPPHDR::~HPPHDR()
{
if (has_device() && get_device().get_handle())
{
vk::Device device = get_device().get_handle();
bloom.destroy(device);
composition.destroy(device);
filter_pass.destroy(device);
models.destroy(device, descriptor_pool);
offscreen.destroy(device);
textures.destroy(device);
}
}
bool HPPHDR::prepare(const vkb::ApplicationOptions &options)
{
assert(!prepared);
if (HPPApiVulkanSample::prepare(options))
{
prepare_camera();
load_assets();
prepare_uniform_buffers();
prepare_offscreen_buffer();
descriptor_pool = create_descriptor_pool();
prepare_bloom();
prepare_composition();
prepare_models();
build_command_buffers();
prepared = true;
}
return prepared;
}
bool HPPHDR::resize(const uint32_t width, const uint32_t height)
{
HPPApiVulkanSample::resize(width, height);
update_uniform_buffers();
return true;
}
void HPPHDR::request_gpu_features(vkb::core::HPPPhysicalDevice &gpu)
{
// Enable anisotropic filtering if supported
if (gpu.get_features().samplerAnisotropy)
{
gpu.get_mutable_requested_features().samplerAnisotropy = true;
}
}
void HPPHDR::build_command_buffers()
{
vk::CommandBufferBeginInfo command_buffer_begin_info;
for (int32_t i = 0; i < draw_cmd_buffers.size(); ++i)
{
vk::CommandBuffer command_buffer = draw_cmd_buffers[i];
command_buffer.begin(command_buffer_begin_info);
{
/*
First pass: Render scene to offscreen framebuffer
*/
std::array<vk::ClearValue, 3> clear_values = {{vk::ClearColorValue(std::array<float, 4>({{0.0f, 0.0f, 0.0f, 0.0f}})),
vk::ClearColorValue(std::array<float, 4>({{0.0f, 0.0f, 0.0f, 0.0f}})),
vk::ClearDepthStencilValue{0.0f, 0}}};
vk::RenderPassBeginInfo render_pass_begin_info{.renderPass = offscreen.render_pass,
.framebuffer = offscreen.framebuffer,
.renderArea = {{0, 0}, offscreen.extent},
.clearValueCount = static_cast<uint32_t>(clear_values.size()),
.pClearValues = clear_values.data()};
command_buffer.beginRenderPass(render_pass_begin_info, vk::SubpassContents::eInline);
vk::Viewport viewport{0.0f, 0.0f, static_cast<float>(offscreen.extent.width), static_cast<float>(offscreen.extent.height), 0.0f, 1.0f};
command_buffer.setViewport(0, viewport);
vk::Rect2D scissor{{0, 0}, offscreen.extent};
command_buffer.setScissor(0, scissor);
// Skybox
if (display_skybox)
{
command_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics, models.skybox.pipeline);
command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, models.pipeline_layout, 0, models.skybox.descriptor_set, {});
draw_model(models.skybox.meshes[0], command_buffer);
}
// 3D object
command_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics, models.objects.pipeline);
command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, models.pipeline_layout, 0, models.objects.descriptor_set, {});
draw_model(models.objects.meshes[models.object_index], command_buffer);
command_buffer.endRenderPass();
}
/*
Second render pass: First bloom pass
*/
if (bloom.enabled)
{
// Bloom filter
vk::ClearValue clear_value(vk::ClearColorValue(std::array<float, 4>({{0.0f, 0.0f, 0.0f, 0.0f}})));
vk::RenderPassBeginInfo render_pass_begin_info{.renderPass = filter_pass.render_pass,
.framebuffer = filter_pass.framebuffer,
.renderArea = {{0, 0}, filter_pass.extent},
.clearValueCount = 1,
.pClearValues = &clear_value};
command_buffer.beginRenderPass(render_pass_begin_info, vk::SubpassContents::eInline);
vk::Viewport viewport{0.0f, 0.0f, static_cast<float>(filter_pass.extent.width), static_cast<float>(filter_pass.extent.height), 0.0f, 1.0f};
command_buffer.setViewport(0, viewport);
vk::Rect2D scissor{{0, 0}, filter_pass.extent};
command_buffer.setScissor(0, scissor);
command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, bloom.pipeline_layout, 0, bloom.descriptor_set, {});
command_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics, bloom.pipelines[1]);
command_buffer.draw(3, 1, 0, 0);
command_buffer.endRenderPass();
}
/*
Note: Explicit synchronization is not required between the render pass, as this is done implicit via sub pass dependencies
*/
/*
Third render pass: Scene rendering with applied second bloom pass (when enabled)
*/
{
// Final composition
std::array<vk::ClearValue, 2> clear_values = {{vk::ClearColorValue(std::array<float, 4>({{0.0f, 0.0f, 0.0f, 0.0f}})),
vk::ClearDepthStencilValue{0.0f, 0}}};
vk::RenderPassBeginInfo render_pass_begin_info{.renderPass = render_pass,
.framebuffer = framebuffers[i],
.renderArea = {{0, 0}, extent},
.clearValueCount = static_cast<uint32_t>(clear_values.size()),
.pClearValues = clear_values.data()};
command_buffer.beginRenderPass(render_pass_begin_info, vk::SubpassContents::eInline);
vk::Viewport viewport{0.0f, 0.0f, static_cast<float>(extent.width), static_cast<float>(extent.height), 0.0f, 1.0f};
command_buffer.setViewport(0, viewport);
vk::Rect2D scissor{{0, 0}, extent};
command_buffer.setScissor(0, scissor);
command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, composition.pipeline_layout, 0, composition.descriptor_set, {});
// Scene
command_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics, composition.pipeline);
command_buffer.draw(3, 1, 0, 0);
// Bloom
if (bloom.enabled)
{
command_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics, bloom.pipelines[0]);
command_buffer.draw(3, 1, 0, 0);
}
draw_ui(command_buffer);
command_buffer.endRenderPass();
}
command_buffer.end();
}
}
void HPPHDR::on_update_ui_overlay(vkb::Drawer &drawer)
{
if (drawer.header("Settings"))
{
if (drawer.combo_box("Object type", &models.object_index, object_names))
{
update_uniform_buffers();
rebuild_command_buffers();
}
if (drawer.input_float("Exposure", &ubo_params.exposure, 0.025f, "%.3f"))
{
update_params();
}
if (drawer.checkbox("Bloom", &bloom.enabled))
{
rebuild_command_buffers();
}
if (drawer.checkbox("Skybox", &display_skybox))
{
rebuild_command_buffers();
}
}
}
void HPPHDR::render(float delta_time)
{
if (prepared)
{
draw();
if (camera.updated)
{
update_uniform_buffers();
}
}
}
vk::DeviceMemory HPPHDR::allocate_memory(vk::Image image)
{
vk::MemoryRequirements memory_requirements = get_device().get_handle().getImageMemoryRequirements(image);
vk::MemoryAllocateInfo memory_allocate_info{.allocationSize = memory_requirements.size,
.memoryTypeIndex = get_device().get_gpu().get_memory_type(memory_requirements.memoryTypeBits,
vk::MemoryPropertyFlagBits::eDeviceLocal)};
return get_device().get_handle().allocateMemory(memory_allocate_info);
}
HPPHDR::FramebufferAttachment HPPHDR::create_attachment(vk::Format format, vk::ImageUsageFlagBits usage)
{
vk::Image image = create_image(format, usage);
vk::DeviceMemory memory = allocate_memory(image);
get_device().get_handle().bindImageMemory(image, memory, 0);
vk::ImageView view =
vkb::common::create_image_view(get_device().get_handle(), image, vk::ImageViewType::e2D, format, vkb::common::get_image_aspect_flags(usage, format));
return {format, image, memory, view};
}
vk::DescriptorPool HPPHDR::create_descriptor_pool()
{
std::array<vk::DescriptorPoolSize, 2> pool_sizes = {{{vk::DescriptorType::eUniformBuffer, 4}, {vk::DescriptorType::eCombinedImageSampler, 6}}};
return get_device().get_handle().createDescriptorPool(
{.maxSets = 4, .poolSizeCount = static_cast<uint32_t>(pool_sizes.size()), .pPoolSizes = pool_sizes.data()});
}
vk::Pipeline HPPHDR::create_bloom_pipeline(uint32_t direction)
{
std::vector<vk::PipelineShaderStageCreateInfo> shader_stages{load_shader("hdr", "bloom.vert.spv", vk::ShaderStageFlagBits::eVertex),
load_shader("hdr", "bloom.frag.spv", vk::ShaderStageFlagBits::eFragment)};
// Set constant parameters via specialization constants
vk::SpecializationMapEntry specialization_map_entry{0, 0, sizeof(uint32_t)};
vk::SpecializationInfo specialization_info{1, &specialization_map_entry, sizeof(uint32_t), &direction};
shader_stages[1].pSpecializationInfo = &specialization_info;
vk::PipelineColorBlendAttachmentState blend_attachment_state{.blendEnable = true,
.srcColorBlendFactor = vk::BlendFactor::eOne,
.dstColorBlendFactor = vk::BlendFactor::eOne,
.colorBlendOp = vk::BlendOp::eAdd,
.srcAlphaBlendFactor = vk::BlendFactor::eSrcAlpha,
.dstAlphaBlendFactor = vk::BlendFactor::eDstAlpha,
.alphaBlendOp = vk::BlendOp::eAdd,
.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG |
vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA};
// Note: Using reversed depth-buffer for increased precision, so Greater depth values are kept
vk::PipelineDepthStencilStateCreateInfo depth_stencil_state;
depth_stencil_state.depthCompareOp = vk::CompareOp::eGreater;
depth_stencil_state.back.compareOp = vk::CompareOp::eAlways;
depth_stencil_state.front = depth_stencil_state.back;
// Empty vertex input state, full screen triangles are generated by the vertex shader
return vkb::common::create_graphics_pipeline(get_device().get_handle(),
pipeline_cache,
shader_stages,
{},
vk::PrimitiveTopology::eTriangleList,
0,
vk::PolygonMode::eFill,
vk::CullModeFlagBits::eFront,
vk::FrontFace::eCounterClockwise,
{blend_attachment_state},
depth_stencil_state,
bloom.pipeline_layout,
direction == 1 ? render_pass : filter_pass.render_pass);
}
vk::Pipeline HPPHDR::create_composition_pipeline()
{
std::vector<vk::PipelineShaderStageCreateInfo> shader_stages{load_shader("hdr", "composition.vert.spv", vk::ShaderStageFlagBits::eVertex),
load_shader("hdr", "composition.frag.spv", vk::ShaderStageFlagBits::eFragment)};
vk::PipelineColorBlendAttachmentState blend_attachment_state{.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG |
vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA};
// Note: Using reversed depth-buffer for increased precision, so Greater depth values are kept
vk::PipelineDepthStencilStateCreateInfo depth_stencil_state;
depth_stencil_state.depthCompareOp = vk::CompareOp::eGreater;
depth_stencil_state.back.compareOp = vk::CompareOp::eAlways;
depth_stencil_state.front = depth_stencil_state.back;
// Empty vertex input state, full screen triangles are generated by the vertex shader
return vkb::common::create_graphics_pipeline(get_device().get_handle(),
pipeline_cache,
shader_stages,
{},
vk::PrimitiveTopology::eTriangleList,
0,
vk::PolygonMode::eFill,
vk::CullModeFlagBits::eFront,
vk::FrontFace::eCounterClockwise,
{blend_attachment_state},
depth_stencil_state,
composition.pipeline_layout,
render_pass);
}
vk::RenderPass HPPHDR::create_filter_render_pass()
{
// Set up separate renderpass with references to the color and depth attachments
vk::AttachmentDescription attachment_description{.format = filter_pass.color.format,
.samples = vk::SampleCountFlagBits::e1,
.loadOp = vk::AttachmentLoadOp::eClear,
.storeOp = vk::AttachmentStoreOp::eStore,
.stencilLoadOp = vk::AttachmentLoadOp::eDontCare,
.stencilStoreOp = vk::AttachmentStoreOp::eDontCare,
.initialLayout = vk::ImageLayout::eUndefined,
.finalLayout = vk::ImageLayout::eShaderReadOnlyOptimal};
vk::AttachmentReference color_reference{0, vk::ImageLayout::eColorAttachmentOptimal};
vk::SubpassDescription subpass{.pipelineBindPoint = vk::PipelineBindPoint::eGraphics, .colorAttachmentCount = 1, .pColorAttachments = &color_reference};
return create_render_pass({attachment_description}, subpass);
}
vk::Image HPPHDR::create_image(vk::Format format, vk::ImageUsageFlagBits usage)
{
vk::ImageCreateInfo image_create_info{.imageType = vk::ImageType::e2D,
.format = format,
.extent = {offscreen.extent.width, offscreen.extent.height, 1},
.mipLevels = 1,
.arrayLayers = 1,
.samples = vk::SampleCountFlagBits::e1,
.tiling = vk::ImageTiling::eOptimal,
.usage = usage | vk::ImageUsageFlagBits::eSampled};
return get_device().get_handle().createImage(image_create_info);
}
vk::Pipeline HPPHDR::create_models_pipeline(uint32_t shaderType, vk::CullModeFlagBits cullMode, bool depthTestAndWrite)
{
std::vector<vk::PipelineShaderStageCreateInfo> shader_stages{load_shader("hdr", "gbuffer.vert.spv", vk::ShaderStageFlagBits::eVertex),
load_shader("hdr", "gbuffer.frag.spv", vk::ShaderStageFlagBits::eFragment)};
// Set constant parameters via specialization constants
vk::SpecializationMapEntry specialization_map_entry{0, 0, sizeof(uint32_t)};
// Set constant parameters via specialization constants
vk::SpecializationInfo specialization_info{1, &specialization_map_entry, sizeof(uint32_t), &shaderType};
shader_stages[0].pSpecializationInfo = &specialization_info;
shader_stages[1].pSpecializationInfo = &specialization_info;
// Vertex bindings an attributes for model rendering
// Binding description
vk::VertexInputBindingDescription vertex_input_binding{0, sizeof(HPPVertex), vk::VertexInputRate::eVertex};
// Attribute descriptions
std::vector<vk::VertexInputAttributeDescription> vertex_input_attributes = {{0, 0, vk::Format::eR32G32B32Sfloat, 0},
{1, 0, vk::Format::eR32G32B32Sfloat, 3 * sizeof(float)}};
vk::PipelineVertexInputStateCreateInfo vertex_input_state{.vertexBindingDescriptionCount = 1,
.pVertexBindingDescriptions = &vertex_input_binding,
.vertexAttributeDescriptionCount = static_cast<uint32_t>(vertex_input_attributes.size()),
.pVertexAttributeDescriptions = vertex_input_attributes.data()};
std::vector<vk::PipelineColorBlendAttachmentState> blend_attachment_states(2);
blend_attachment_states[0].colorWriteMask =
vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA;
blend_attachment_states[1].colorWriteMask = blend_attachment_states[0].colorWriteMask;
// Note: Using reversed depth-buffer for increased precision, so Greater depth values are kept
vk::PipelineDepthStencilStateCreateInfo depth_stencil_state;
depth_stencil_state.depthCompareOp = vk::CompareOp::eGreater;
depth_stencil_state.depthWriteEnable = depthTestAndWrite;
depth_stencil_state.depthTestEnable = depthTestAndWrite;
depth_stencil_state.back.compareOp = vk::CompareOp::eAlways;
depth_stencil_state.front = depth_stencil_state.back;
return vkb::common::create_graphics_pipeline(get_device().get_handle(),
pipeline_cache,
shader_stages,
vertex_input_state,
vk::PrimitiveTopology::eTriangleList,
0,
vk::PolygonMode::eFill,
cullMode,
vk::FrontFace::eCounterClockwise,
blend_attachment_states,
depth_stencil_state,
models.pipeline_layout,
offscreen.render_pass);
}
vk::RenderPass HPPHDR::create_offscreen_render_pass()
{
// Set up separate renderpass with references to the color and depth attachments
std::vector<vk::AttachmentDescription> attachment_descriptions(3);
// Init attachment properties
for (uint32_t i = 0; i < 3; ++i)
{
attachment_descriptions[i].samples = vk::SampleCountFlagBits::e1;
attachment_descriptions[i].loadOp = vk::AttachmentLoadOp::eClear;
attachment_descriptions[i].storeOp = vk::AttachmentStoreOp::eStore;
attachment_descriptions[i].stencilLoadOp = vk::AttachmentLoadOp::eDontCare;
attachment_descriptions[i].stencilStoreOp = vk::AttachmentStoreOp::eDontCare;
attachment_descriptions[i].initialLayout = vk::ImageLayout::eUndefined;
attachment_descriptions[i].finalLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
}
attachment_descriptions[2].finalLayout = vk::ImageLayout::eDepthStencilAttachmentOptimal;
// Formats
attachment_descriptions[0].format = offscreen.color[0].format;
attachment_descriptions[1].format = offscreen.color[1].format;
attachment_descriptions[2].format = offscreen.depth.format;
std::array<vk::AttachmentReference, 2> color_references{{{0, vk::ImageLayout::eColorAttachmentOptimal},
{1, vk::ImageLayout::eColorAttachmentOptimal}}};
vk::AttachmentReference depth_reference{2, vk::ImageLayout::eDepthStencilAttachmentOptimal};
vk::SubpassDescription subpass{.pipelineBindPoint = vk::PipelineBindPoint::eGraphics,
.colorAttachmentCount = static_cast<uint32_t>(color_references.size()),
.pColorAttachments = color_references.data(),
.pDepthStencilAttachment = &depth_reference};
return create_render_pass(attachment_descriptions, subpass);
}
vk::RenderPass HPPHDR::create_render_pass(std::vector<vk::AttachmentDescription> const &attachment_descriptions, vk::SubpassDescription const &subpass_description)
{
// Use subpass dependencies for attachment layout transitions
std::array<vk::SubpassDependency, 2> subpass_dependencies;
subpass_dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
subpass_dependencies[0].dstSubpass = 0;
// End of previous commands
subpass_dependencies[0].srcStageMask = vk::PipelineStageFlagBits::eBottomOfPipe;
subpass_dependencies[0].srcAccessMask = vk::AccessFlagBits::eNoneKHR;
// Read/write from/to depth
subpass_dependencies[0].dstStageMask = vk::PipelineStageFlagBits::eEarlyFragmentTests;
subpass_dependencies[0].dstAccessMask = vk::AccessFlagBits::eDepthStencilAttachmentRead | vk::AccessFlagBits::eDepthStencilAttachmentWrite;
// Write to attachment
subpass_dependencies[0].dstStageMask |= vk::PipelineStageFlagBits::eColorAttachmentOutput;
subpass_dependencies[0].dstAccessMask |= vk::AccessFlagBits::eColorAttachmentWrite;
subpass_dependencies[1].srcSubpass = 0;
subpass_dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL;
// End of write to attachment
subpass_dependencies[1].srcStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput;
subpass_dependencies[1].srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite;
// Attachment later read using sampler in 'bloom[0]' pipeline
subpass_dependencies[1].dstStageMask = vk::PipelineStageFlagBits::eFragmentShader;
subpass_dependencies[1].dstAccessMask = vk::AccessFlagBits::eShaderRead;
vk::RenderPassCreateInfo render_pass_create_info{.attachmentCount = static_cast<uint32_t>(attachment_descriptions.size()),
.pAttachments = attachment_descriptions.data(),
.subpassCount = 1,
.pSubpasses = &subpass_description,
.dependencyCount = static_cast<uint32_t>(subpass_dependencies.size()),
.pDependencies = subpass_dependencies.data()};
return get_device().get_handle().createRenderPass(render_pass_create_info);
}
void HPPHDR::draw()
{
HPPApiVulkanSample::prepare_frame();
submit_info.setCommandBuffers(draw_cmd_buffers[current_buffer]);
queue.submit(submit_info);
HPPApiVulkanSample::submit_frame();
}
void HPPHDR::load_assets()
{
// Models
models.skybox.meshes.emplace_back(load_model("scenes/cube.gltf"));
std::vector<std::string> filenames = {"geosphere.gltf", "teapot.gltf", "torusknot.gltf"};
object_names = {"Sphere", "Teapot", "Torusknot"};
for (auto file : filenames)
{
models.objects.meshes.emplace_back(load_model("scenes/" + file));
}
// Transforms
auto geosphere_matrix = glm::mat4(1.0f);
models.transforms.push_back(geosphere_matrix);
auto teapot_matrix = glm::mat4(1.0f);
teapot_matrix = glm::scale(teapot_matrix, glm::vec3(10.0f, 10.0f, 10.0f));
teapot_matrix = glm::rotate(teapot_matrix, glm::radians(180.0f), glm::vec3(1.0f, 0.0f, 0.0f));
models.transforms.push_back(teapot_matrix);
auto torus_matrix = glm::mat4(1.0f);
models.transforms.push_back(torus_matrix);
// Load HDR cube map
textures.envmap = load_texture_cubemap("textures/uffizi_rgba16f_cube.ktx", vkb::scene_graph::components::HPPImage::Color);
}
void HPPHDR::prepare_bloom()
{
std::array<vk::DescriptorSetLayoutBinding, 2> bindings = {{{0, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment},
{1, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment}}};
vk::Device device = get_device().get_handle();
bloom.descriptor_set_layout = device.createDescriptorSetLayout({.bindingCount = static_cast<uint32_t>(bindings.size()), .pBindings = bindings.data()});
bloom.pipeline_layout = device.createPipelineLayout({.setLayoutCount = 1, .pSetLayouts = &bloom.descriptor_set_layout});
bloom.pipelines[0] = create_bloom_pipeline(1);
bloom.pipelines[1] = create_bloom_pipeline(0);
bloom.descriptor_set = vkb::common::allocate_descriptor_set(device, descriptor_pool, bloom.descriptor_set_layout);
update_bloom_descriptor_set();
}
void HPPHDR::prepare_camera()
{
camera.type = vkb::CameraType::LookAt;
camera.set_position(glm::vec3(0.0f, 0.0f, -4.0f));
camera.set_rotation(glm::vec3(0.0f, 180.0f, 0.0f));
// Note: Using reversed depth-buffer for increased precision, so Znear and Zfar are flipped
camera.set_perspective(60.0f, static_cast<float>(extent.width) / static_cast<float>(extent.height), 256.0f, 0.1f);
}
void HPPHDR::prepare_composition()
{
std::array<vk::DescriptorSetLayoutBinding, 2> bindings = {{{0, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment},
{1, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment}}};
vk::Device device = get_device().get_handle();
composition.descriptor_set_layout =
device.createDescriptorSetLayout({.bindingCount = static_cast<uint32_t>(bindings.size()), .pBindings = bindings.data()});
composition.pipeline_layout = device.createPipelineLayout({.setLayoutCount = 1, .pSetLayouts = &composition.descriptor_set_layout});
composition.pipeline = create_composition_pipeline();
composition.descriptor_set = vkb::common::allocate_descriptor_set(device, descriptor_pool, composition.descriptor_set_layout);
update_composition_descriptor_set();
}
void HPPHDR::prepare_models()
{
std::array<vk::DescriptorSetLayoutBinding, 3> bindings = {{{0, vk::DescriptorType::eUniformBuffer, 1, vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment},
{1, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment},
{2, vk::DescriptorType::eUniformBuffer, 1, vk::ShaderStageFlagBits::eFragment}}};
vk::Device device = get_device().get_handle();
models.descriptor_set_layout = device.createDescriptorSetLayout({.bindingCount = static_cast<uint32_t>(bindings.size()), .pBindings = bindings.data()});
models.pipeline_layout = device.createPipelineLayout({.setLayoutCount = 1, .pSetLayouts = &models.descriptor_set_layout});
models.objects.descriptor_set = vkb::common::allocate_descriptor_set(device, descriptor_pool, models.descriptor_set_layout);
update_model_descriptor_set(models.objects.descriptor_set);
models.objects.pipeline = create_models_pipeline(1, vk::CullModeFlagBits::eFront, true);
models.skybox.descriptor_set = vkb::common::allocate_descriptor_set(device, descriptor_pool, models.descriptor_set_layout);
update_model_descriptor_set(models.skybox.descriptor_set);
models.skybox.pipeline = create_models_pipeline(0, vk::CullModeFlagBits::eBack, false);
}
// Prepare a new framebuffer and attachments for offscreen rendering (G-Buffer)
void HPPHDR::prepare_offscreen_buffer()
{
// We need to select a format that supports the color attachment blending flag, so we iterate over multiple formats to find one that supports this flag
const std::vector<vk::Format> float_format_priority_list = {
vk::Format::eR32G32B32A32Sfloat,
vk::Format::eR16G16B16A16Sfloat // Guaranteed blend support for this
};
vk::Format color_format = vkb::common::choose_blendable_format(get_device().get_gpu().get_handle(), float_format_priority_list);
{
offscreen.extent = extent;
// Color attachments
// We are using two 128-Bit RGBA floating point color buffers for this sample
// In a performance or bandwidth-limited scenario you should consider using a format with lower precision
offscreen.color[0] = create_attachment(color_format, vk::ImageUsageFlagBits::eColorAttachment);
offscreen.color[1] = create_attachment(color_format, vk::ImageUsageFlagBits::eColorAttachment);
// Depth attachment
offscreen.depth = create_attachment(depth_format, vk::ImageUsageFlagBits::eDepthStencilAttachment);
offscreen.render_pass = create_offscreen_render_pass();
offscreen.framebuffer = vkb::common::create_framebuffer(
get_device().get_handle(), offscreen.render_pass, {offscreen.color[0].view, offscreen.color[1].view, offscreen.depth.view}, offscreen.extent);
// Create sampler to sample from the color attachments
offscreen.sampler = vkb::common::create_sampler(get_device().get_gpu().get_handle(), get_device().get_handle(), color_format,
vk::Filter::eNearest, vk::SamplerAddressMode::eClampToEdge, 1.0f, 1.0f);
}
// Bloom separable filter pass
{
filter_pass.extent = extent;
// Color attachments
// Floating point color attachment
filter_pass.color = create_attachment(color_format, vk::ImageUsageFlagBits::eColorAttachment);
filter_pass.render_pass = create_filter_render_pass();
filter_pass.framebuffer = vkb::common::create_framebuffer(get_device().get_handle(), filter_pass.render_pass, {filter_pass.color.view}, filter_pass.extent);
filter_pass.sampler = vkb::common::create_sampler(get_device().get_gpu().get_handle(), get_device().get_handle(),
color_format, vk::Filter::eNearest, vk::SamplerAddressMode::eClampToEdge, 1.0f, 1.0f);
}
}
// Prepare and initialize uniform buffer containing shader uniforms
void HPPHDR::prepare_uniform_buffers()
{
// Matrices vertex shader uniform buffer
uniform_buffers.matrices = std::make_unique<vkb::core::BufferCpp>(get_device(),
sizeof(ubo_matrices),
vk::BufferUsageFlagBits::eUniformBuffer,
VMA_MEMORY_USAGE_CPU_TO_GPU);
// Params
uniform_buffers.params = std::make_unique<vkb::core::BufferCpp>(get_device(),
sizeof(ubo_params),
vk::BufferUsageFlagBits::eUniformBuffer,
VMA_MEMORY_USAGE_CPU_TO_GPU);
update_uniform_buffers();
update_params();
}
void HPPHDR::update_composition_descriptor_set()
{
std::array<vk::DescriptorImageInfo, 2> color_descriptors = {{{offscreen.sampler, offscreen.color[0].view, vk::ImageLayout::eShaderReadOnlyOptimal},
{offscreen.sampler, filter_pass.color.view, vk::ImageLayout::eShaderReadOnlyOptimal}}};
std::array<vk::WriteDescriptorSet, 2> sampler_write_descriptor_sets = {{{.dstSet = composition.descriptor_set,
.dstBinding = 0,
.descriptorCount = 1,
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
.pImageInfo = &color_descriptors[0]},
{.dstSet = composition.descriptor_set,
.dstBinding = 1,
.descriptorCount = 1,
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
.pImageInfo = &color_descriptors[1]}}};
get_device().get_handle().updateDescriptorSets(sampler_write_descriptor_sets, {});
}
void HPPHDR::update_bloom_descriptor_set()
{
std::array<vk::DescriptorImageInfo, 2> color_descriptors = {{{offscreen.sampler, offscreen.color[0].view, vk::ImageLayout::eShaderReadOnlyOptimal},
{offscreen.sampler, offscreen.color[1].view, vk::ImageLayout::eShaderReadOnlyOptimal}}};
std::array<vk::WriteDescriptorSet, 2> sampler_write_descriptor_sets = {{{.dstSet = bloom.descriptor_set,
.dstBinding = 0,
.descriptorCount = 1,
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
.pImageInfo = &color_descriptors[0]},
{.dstSet = bloom.descriptor_set,
.dstBinding = 1,
.descriptorCount = 1,
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
.pImageInfo = &color_descriptors[1]}}};
get_device().get_handle().updateDescriptorSets(sampler_write_descriptor_sets, {});
}
void HPPHDR::update_model_descriptor_set(vk::DescriptorSet descriptor_set)
{
vk::DescriptorBufferInfo matrix_buffer_descriptor{uniform_buffers.matrices->get_handle(), 0, vk::WholeSize};
vk::DescriptorImageInfo environment_image_descriptor{textures.envmap.sampler,
textures.envmap.image->get_vk_image_view().get_handle(),
descriptor_type_to_image_layout(vk::DescriptorType::eCombinedImageSampler,
textures.envmap.image->get_vk_image_view().get_format())};
vk::DescriptorBufferInfo params_buffer_descriptor{uniform_buffers.params->get_handle(), 0, vk::WholeSize};
std::array<vk::WriteDescriptorSet, 3> write_descriptor_sets = {{{.dstSet = descriptor_set,
.dstBinding = 0,
.descriptorCount = 1,
.descriptorType = vk::DescriptorType::eUniformBuffer,
.pBufferInfo = &matrix_buffer_descriptor},
{.dstSet = descriptor_set,
.dstBinding = 1,
.descriptorCount = 1,
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
.pImageInfo = &environment_image_descriptor},
{.dstSet = descriptor_set,
.dstBinding = 2,
.descriptorCount = 1,
.descriptorType = vk::DescriptorType::eUniformBuffer,
.pBufferInfo = &params_buffer_descriptor}}};
get_device().get_handle().updateDescriptorSets(write_descriptor_sets, {});
}
void HPPHDR::update_params()
{
uniform_buffers.params->convert_and_update(ubo_params);
}
void HPPHDR::update_uniform_buffers()
{
ubo_matrices.projection = camera.matrices.perspective;
ubo_matrices.modelview = camera.matrices.view * models.transforms[models.object_index];
ubo_matrices.skybox_modelview = camera.matrices.view;
ubo_matrices.inverse_modelview = glm::inverse(camera.matrices.view);
uniform_buffers.matrices->convert_and_update(ubo_matrices);
}
std::unique_ptr<vkb::Application> create_hpp_hdr()
{
return std::make_unique<HPPHDR>();
}