init
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
# Copyright (c) 2024, Google
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
get_filename_component(FOLDER_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
|
||||
get_filename_component(PARENT_DIR ${CMAKE_CURRENT_LIST_DIR} PATH)
|
||||
get_filename_component(CATEGORY_NAME ${PARENT_DIR} NAME)
|
||||
|
||||
add_sample(
|
||||
ID ${FOLDER_NAME}
|
||||
CATEGORY ${CATEGORY_NAME}
|
||||
AUTHOR "Google"
|
||||
NAME "HPP Order-independent transparency (depth peeling)"
|
||||
DESCRIPTION "Order-independent transparency using depth peeling using Vulkan-Hpp"
|
||||
SHADER_FILES_GLSL
|
||||
"oit_depth_peeling/background.frag"
|
||||
"oit_depth_peeling/combine.frag"
|
||||
"oit_depth_peeling/fullscreen.vert"
|
||||
"oit_depth_peeling/gather.frag"
|
||||
"oit_depth_peeling/gather.vert"
|
||||
"oit_depth_peeling/gather_first.frag")
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
////
|
||||
- Copyright (c) 2024, Google
|
||||
-
|
||||
- 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.
|
||||
-
|
||||
////
|
||||
|
||||
= Order-independent transparency with depth peeling using Vulkan-Hpp
|
||||
|
||||
ifdef::site-gen-antora[]
|
||||
TIP: The source for this sample can be found in the https://github.com/KhronosGroup/Vulkan-Samples/tree/main/samples/api/hpp_oit_depth_peeling[Khronos Vulkan samples github repository].
|
||||
endif::[]
|
||||
|
||||
NOTE: This is a transcoded version of the API sample https://github.com/KhronosGroup/Vulkan-Samples/tree/main/samples/api/oit_depth_peeling[OIT depth peeling] that illustrates the usage of the C{pp} bindings of vulkan provided by vulkan.hpp. Please see there for the documentation on this sample.
|
||||
|
||||
:pp: {plus}{plus}
|
||||
|
||||
image::samples/api/oit_depth_peeling/images/sample.png[Sample]
|
||||
|
||||
== Overview
|
||||
|
||||
This sample implements an order-independent transparency (OIT) algorithm using depth peeling.
|
||||
It renders a single torus whose opacity can be controlled via the UI.
|
||||
It produces pixel-perfect results.
|
||||
It is based on the https://developer.download.nvidia.com/assets/gamedev/docs/OrderIndependentTransparency.pdf[original paper] from Cass Everitt.
|
||||
|
||||
== Algorithm
|
||||
|
||||
The OIT algorithm consists of several _gather_ passes followed by one _combine_ pass.
|
||||
|
||||
Each _gather_ pass renders one layer of transparent geometry.
|
||||
The first pass renders the first layer, the second pass the second layer, etc.
|
||||
The N^th^ layer consists of all the N^th^ fragments of each pixel when the fragments are ordered from front to back.
|
||||
|
||||
The _combine_ pass is a screen-space operation.
|
||||
It merges the layer images from back to front to produce the final result.
|
||||
|
||||
The algorithm can produce pixel-perfect results, even with intersecting geometry.
|
||||
When there are more geometry layers than gather passes, the backmost layers get skipped, but the visual results stay stable (i.e. no flickering pixels).
|
||||
|
||||
== Options
|
||||
|
||||
[cols="2,4,4"]
|
||||
|===
|
||||
| Option | Description | Comments
|
||||
|
||||
| Camera auto-rotation
|
||||
| Enable the automatic rotation of the camera
|
||||
|
|
||||
|
||||
| Background grayscale
|
||||
| Specify the grayscale value by which the background color is multiplied (0.0 to 1.0)
|
||||
|
|
||||
|
||||
| Object alpha
|
||||
| Specify the opacity of the transparent object (0.0 to 1.0)
|
||||
|
|
||||
|
||||
| Front layer index
|
||||
| The first layer to be rendered (0 to 7).
|
||||
|
|
||||
|
||||
| Back layer index
|
||||
| The last layer to be rendered (0 to 7).
|
||||
| This cannot be less that the front layer index.
|
||||
|
||||
|===
|
||||
|
||||
@@ -0,0 +1,595 @@
|
||||
/* Copyright (c) 2024-2025, Google
|
||||
* Copyright (c) 2024-2025, NVIDIA
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "hpp_oit_depth_peeling.h"
|
||||
|
||||
HPPOITDepthPeeling::HPPOITDepthPeeling()
|
||||
{}
|
||||
|
||||
HPPOITDepthPeeling::~HPPOITDepthPeeling()
|
||||
{
|
||||
if (has_device())
|
||||
{
|
||||
vk::Device device = get_device().get_handle();
|
||||
background.destroy(device);
|
||||
combinePass.destroy(device);
|
||||
for (auto &d : depths)
|
||||
{
|
||||
d.destroy();
|
||||
}
|
||||
device.destroyDescriptorPool(descriptor_pool);
|
||||
gatherPass.destroy(device);
|
||||
for (auto &l : layers)
|
||||
{
|
||||
l.destroy(device);
|
||||
}
|
||||
model.reset();
|
||||
device.destroySampler(point_sampler);
|
||||
scene_constants.reset();
|
||||
}
|
||||
}
|
||||
|
||||
bool HPPOITDepthPeeling::prepare(const vkb::ApplicationOptions &options)
|
||||
{
|
||||
assert(!prepared);
|
||||
|
||||
if (HPPApiVulkanSample::prepare(options))
|
||||
{
|
||||
prepare_camera();
|
||||
load_assets();
|
||||
create_point_sampler();
|
||||
create_scene_constants_buffer();
|
||||
create_descriptor_pool();
|
||||
create_combine_pass();
|
||||
create_images(extent.width, extent.height);
|
||||
create_gather_pass();
|
||||
create_background_pipeline();
|
||||
update_scene_constants();
|
||||
update_descriptors();
|
||||
build_command_buffers();
|
||||
|
||||
prepared = true;
|
||||
}
|
||||
return prepared;
|
||||
}
|
||||
|
||||
bool HPPOITDepthPeeling::resize(const uint32_t width, const uint32_t height)
|
||||
{
|
||||
create_images(width, height);
|
||||
create_gather_pass_framebuffers(width, height);
|
||||
update_descriptors();
|
||||
return HPPApiVulkanSample::resize(width, height);
|
||||
}
|
||||
|
||||
void HPPOITDepthPeeling::request_gpu_features(vkb::core::HPPPhysicalDevice &gpu)
|
||||
{
|
||||
if (gpu.get_features().samplerAnisotropy)
|
||||
{
|
||||
gpu.get_mutable_requested_features().samplerAnisotropy = VK_TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("This sample requires support for anisotropic sampling");
|
||||
}
|
||||
}
|
||||
|
||||
void HPPOITDepthPeeling::build_command_buffers()
|
||||
{
|
||||
vk::CommandBufferBeginInfo command_buffer_begin_info;
|
||||
|
||||
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{.renderArea = {{0, 0}, extent},
|
||||
.clearValueCount = static_cast<uint32_t>(clear_values.size()),
|
||||
.pClearValues = clear_values.data()};
|
||||
vk::Viewport viewport{0.0f, 0.0f, static_cast<float>(extent.width), static_cast<float>(extent.height), 0.0f, 1.0f};
|
||||
vk::Rect2D scissor{{0, 0}, extent};
|
||||
|
||||
for (int32_t i = 0; i < draw_cmd_buffers.size(); ++i)
|
||||
{
|
||||
auto const &command_buffer = draw_cmd_buffers[i];
|
||||
command_buffer.begin(command_buffer_begin_info);
|
||||
{
|
||||
// Gather passes
|
||||
// Each pass renders a single transparent layer into a layer texture.
|
||||
for (uint32_t l = 0; l <= gui.layer_index_back; ++l)
|
||||
{
|
||||
// Two depth textures are used.
|
||||
// Their roles alternates for each pass.
|
||||
// The first depth texture is used for fixed-function depth test.
|
||||
// The second one is the result of the depth test from the previous gatherPass pass.
|
||||
// It is bound as texture and read in the shader to discard fragments from the
|
||||
// previous layers.
|
||||
vk::ImageSubresourceRange depth_subresource_range = {vk::ImageAspectFlagBits::eDepth, 0, 1, 0, 1};
|
||||
vkb::common::image_layout_transition(command_buffer,
|
||||
depths[l % kDepthCount].image->get_handle(),
|
||||
vk::PipelineStageFlagBits::eFragmentShader,
|
||||
vk::PipelineStageFlagBits::eEarlyFragmentTests | vk::PipelineStageFlagBits::eLateFragmentTests,
|
||||
vk::AccessFlagBits::eShaderRead,
|
||||
vk::AccessFlagBits::eDepthStencilAttachmentRead | vk::AccessFlagBits::eDepthStencilAttachmentWrite,
|
||||
l <= 1 ? vk::ImageLayout::eUndefined : vk::ImageLayout::eDepthStencilReadOnlyOptimal,
|
||||
vk::ImageLayout::eDepthStencilAttachmentOptimal,
|
||||
depth_subresource_range);
|
||||
if (l > 0)
|
||||
{
|
||||
vkb::common::image_layout_transition(command_buffer,
|
||||
depths[(l + 1) % kDepthCount].image->get_handle(),
|
||||
vk::PipelineStageFlagBits::eEarlyFragmentTests | vk::PipelineStageFlagBits::eLateFragmentTests,
|
||||
vk::PipelineStageFlagBits::eFragmentShader,
|
||||
vk::AccessFlagBits::eDepthStencilAttachmentRead | vk::AccessFlagBits::eDepthStencilAttachmentWrite,
|
||||
vk::AccessFlagBits::eShaderRead,
|
||||
vk::ImageLayout::eDepthStencilAttachmentOptimal,
|
||||
vk::ImageLayout::eDepthStencilReadOnlyOptimal,
|
||||
depth_subresource_range);
|
||||
}
|
||||
|
||||
// Set one of the layer textures as color attachment, as the gatherPass pass will render to it.
|
||||
vk::ImageSubresourceRange layer_subresource_range = {vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1};
|
||||
vkb::common::image_layout_transition(command_buffer,
|
||||
layers[l].image->get_handle(),
|
||||
vk::PipelineStageFlagBits::eFragmentShader,
|
||||
vk::PipelineStageFlagBits::eColorAttachmentOutput,
|
||||
vk::AccessFlagBits::eShaderRead,
|
||||
vk::AccessFlagBits::eColorAttachmentWrite,
|
||||
vk::ImageLayout::eUndefined,
|
||||
vk::ImageLayout::eColorAttachmentOptimal,
|
||||
layer_subresource_range);
|
||||
|
||||
render_pass_begin_info.framebuffer = layers[l].gather_framebuffer;
|
||||
render_pass_begin_info.renderPass = gatherPass.render_pass;
|
||||
command_buffer.beginRenderPass(render_pass_begin_info, vk::SubpassContents::eInline);
|
||||
{
|
||||
// Render the geometry into the layer texture.
|
||||
command_buffer.setViewport(0, viewport);
|
||||
command_buffer.setScissor(0, scissor);
|
||||
|
||||
command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, gatherPass.pipeline_layout, 0, depths[l % kDepthCount].gather_descriptor_set, {});
|
||||
|
||||
command_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics, l == 0 ? gatherPass.first_pipeline : gatherPass.pipeline);
|
||||
draw_model(model, command_buffer);
|
||||
}
|
||||
command_buffer.endRenderPass();
|
||||
|
||||
// Get the layer texture ready to be read by the combinePass pass.
|
||||
vkb::common::image_layout_transition(command_buffer,
|
||||
layers[l].image->get_handle(),
|
||||
vk::PipelineStageFlagBits::eColorAttachmentOutput,
|
||||
vk::PipelineStageFlagBits::eFragmentShader,
|
||||
vk::AccessFlagBits::eColorAttachmentWrite,
|
||||
vk::AccessFlagBits::eShaderRead,
|
||||
vk::ImageLayout::eColorAttachmentOptimal,
|
||||
vk::ImageLayout::eShaderReadOnlyOptimal,
|
||||
layer_subresource_range);
|
||||
}
|
||||
|
||||
// Combine pass
|
||||
// This pass blends all the layers into the final transparent color.
|
||||
// The final color is then alpha blended into the background.
|
||||
render_pass_begin_info.framebuffer = framebuffers[i];
|
||||
render_pass_begin_info.renderPass = render_pass;
|
||||
command_buffer.beginRenderPass(render_pass_begin_info, vk::SubpassContents::eInline);
|
||||
{
|
||||
command_buffer.setViewport(0, viewport);
|
||||
command_buffer.setScissor(0, scissor);
|
||||
|
||||
command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, combinePass.pipeline_layout, 0, combinePass.descriptor_set, {});
|
||||
|
||||
command_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics, background.pipeline);
|
||||
command_buffer.draw(3, 1, 0, 0);
|
||||
|
||||
command_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics, combinePass.pipeline);
|
||||
command_buffer.draw(3, 1, 0, 0);
|
||||
|
||||
draw_ui(command_buffer);
|
||||
}
|
||||
command_buffer.endRenderPass();
|
||||
}
|
||||
command_buffer.end();
|
||||
}
|
||||
}
|
||||
|
||||
void HPPOITDepthPeeling::on_update_ui_overlay(vkb::Drawer &drawer)
|
||||
{
|
||||
drawer.checkbox("Camera auto-rotation", &gui.camera_auto_rotation);
|
||||
drawer.slider_float("Background grayscale", &gui.background_grayscale, kBackgroundGrayscaleMin, kBackgroundGrayscaleMax);
|
||||
drawer.slider_float("Object opacity", &gui.object_opacity, kObjectAlphaMin, kObjectAlphaMax);
|
||||
|
||||
drawer.slider_int("Front layer index", &gui.layer_index_front, 0, gui.layer_index_back);
|
||||
drawer.slider_int("Back layer index", &gui.layer_index_back, gui.layer_index_front, kLayerMaxCount - 1);
|
||||
}
|
||||
|
||||
void HPPOITDepthPeeling::render(float delta_time)
|
||||
{
|
||||
if (prepared)
|
||||
{
|
||||
HPPApiVulkanSample::prepare_frame();
|
||||
submit_info.setCommandBuffers(draw_cmd_buffers[current_buffer]);
|
||||
queue.submit(submit_info);
|
||||
HPPApiVulkanSample::submit_frame();
|
||||
|
||||
if (gui.camera_auto_rotation)
|
||||
{
|
||||
camera.rotate({delta_time * 5.0f, delta_time * 5.0f, 0.0f});
|
||||
}
|
||||
update_scene_constants();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void HPPOITDepthPeeling::create_background_pipeline()
|
||||
{
|
||||
std::vector<vk::PipelineShaderStageCreateInfo> shader_stages = {load_shader("oit_depth_peeling/fullscreen.vert.spv", vk::ShaderStageFlagBits::eVertex),
|
||||
load_shader("oit_depth_peeling/background.frag.spv", vk::ShaderStageFlagBits::eFragment)};
|
||||
|
||||
vk::VertexInputBindingDescription vertex_input_binding{0, sizeof(HPPVertex), vk::VertexInputRate::eVertex};
|
||||
std::array<vk::VertexInputAttributeDescription, 2> vertex_input_attributes = {{{0, 0, vk::Format::eR32G32B32Sfloat, offsetof(HPPVertex, pos)},
|
||||
{1, 0, vk::Format::eR32G32Sfloat, offsetof(HPPVertex, uv)}}};
|
||||
|
||||
vk::PipelineColorBlendAttachmentState blend_attachment_state{.colorWriteMask = vk::FlagTraits<vk::ColorComponentFlagBits>::allFlags};
|
||||
|
||||
vk::PipelineDepthStencilStateCreateInfo depth_stencil_state{.depthCompareOp = vk::CompareOp::eGreater, .back = {.compareOp = vk::CompareOp::eAlways}};
|
||||
|
||||
background.pipeline = vkb::common::create_graphics_pipeline(get_device().get_handle(),
|
||||
pipeline_cache,
|
||||
shader_stages,
|
||||
{},
|
||||
vk::PrimitiveTopology::eTriangleList,
|
||||
{},
|
||||
vk::PolygonMode::eFill,
|
||||
vk::CullModeFlagBits::eNone,
|
||||
vk::FrontFace::eCounterClockwise,
|
||||
{blend_attachment_state},
|
||||
depth_stencil_state,
|
||||
combinePass.pipeline_layout,
|
||||
render_pass);
|
||||
}
|
||||
|
||||
void HPPOITDepthPeeling::create_combine_pass()
|
||||
{
|
||||
vk::Device device = get_device().get_handle();
|
||||
|
||||
std::array<vk::DescriptorSetLayoutBinding, 3> set_layout_bindings = {
|
||||
{{0, vk::DescriptorType::eUniformBuffer, 1, vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment},
|
||||
{1, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment},
|
||||
{2, vk::DescriptorType::eCombinedImageSampler, kLayerMaxCount, vk::ShaderStageFlagBits::eFragment}}};
|
||||
combinePass.descriptor_set_layout = device.createDescriptorSetLayout({.bindingCount = static_cast<uint32_t>(set_layout_bindings.size()), .pBindings = set_layout_bindings.data()});
|
||||
|
||||
combinePass.descriptor_set = vkb::common::allocate_descriptor_set(device, descriptor_pool, combinePass.descriptor_set_layout);
|
||||
|
||||
combinePass.pipeline_layout = device.createPipelineLayout({.setLayoutCount = 1, .pSetLayouts = &combinePass.descriptor_set_layout});
|
||||
|
||||
create_combine_pass_pipeline();
|
||||
}
|
||||
|
||||
void HPPOITDepthPeeling::create_combine_pass_pipeline()
|
||||
{
|
||||
std::vector<vk::PipelineShaderStageCreateInfo> shader_stages = {load_shader("oit_depth_peeling/fullscreen.vert.spv", vk::ShaderStageFlagBits::eVertex),
|
||||
load_shader("oit_depth_peeling/combine.frag.spv", vk::ShaderStageFlagBits::eFragment)};
|
||||
|
||||
vk::VertexInputBindingDescription vertex_input_binding{0, sizeof(HPPVertex), vk::VertexInputRate::eVertex};
|
||||
std::array<vk::VertexInputAttributeDescription, 2> vertex_input_attributes = {{{0, 0, vk::Format::eR32G32B32Sfloat, offsetof(HPPVertex, pos)},
|
||||
{1, 0, vk::Format::eR32G32Sfloat, offsetof(HPPVertex, uv)}}};
|
||||
|
||||
vk::PipelineColorBlendAttachmentState blend_attachment_state{true,
|
||||
vk::BlendFactor::eSrcAlpha,
|
||||
vk::BlendFactor::eOneMinusSrcColor,
|
||||
vk::BlendOp::eAdd,
|
||||
vk::BlendFactor::eOne,
|
||||
vk::BlendFactor::eZero,
|
||||
vk::BlendOp::eAdd,
|
||||
vk::FlagTraits<vk::ColorComponentFlagBits>::allFlags};
|
||||
|
||||
vk::PipelineDepthStencilStateCreateInfo depth_stencil_state{.depthCompareOp = vk::CompareOp::eGreater, .back = {.compareOp = vk::CompareOp::eAlways}};
|
||||
|
||||
combinePass.pipeline = vkb::common::create_graphics_pipeline(get_device().get_handle(),
|
||||
pipeline_cache,
|
||||
shader_stages,
|
||||
{},
|
||||
vk::PrimitiveTopology::eTriangleList,
|
||||
{},
|
||||
vk::PolygonMode::eFill,
|
||||
vk::CullModeFlagBits::eNone,
|
||||
vk::FrontFace::eCounterClockwise,
|
||||
{blend_attachment_state},
|
||||
depth_stencil_state,
|
||||
combinePass.pipeline_layout,
|
||||
render_pass);
|
||||
}
|
||||
|
||||
void HPPOITDepthPeeling::create_descriptor_pool()
|
||||
{
|
||||
const uint32_t num_gather_pass_combined_image_sampler = kDepthCount;
|
||||
const uint32_t num_gather_pass_uniform_buffer = kDepthCount;
|
||||
|
||||
const uint32_t num_combine_pass_combined_image_sampler = kLayerMaxCount + 1;
|
||||
const uint32_t num_combine_pass_uniform_buffer = 1;
|
||||
|
||||
const uint32_t num_uniform_buffer_descriptors = num_gather_pass_uniform_buffer + num_combine_pass_uniform_buffer;
|
||||
const uint32_t num_combined_image_sampler_descriptors = num_gather_pass_combined_image_sampler + num_combine_pass_combined_image_sampler;
|
||||
std::array<vk::DescriptorPoolSize, 2> pool_sizes = {{{vk::DescriptorType::eUniformBuffer, num_uniform_buffer_descriptors},
|
||||
{vk::DescriptorType::eCombinedImageSampler, num_combined_image_sampler_descriptors}}};
|
||||
const uint32_t num_gather_descriptor_sets = 2;
|
||||
const uint32_t num_combine_descriptor_sets = 1;
|
||||
const uint32_t num_descriptor_sets = num_gather_descriptor_sets + num_combine_descriptor_sets;
|
||||
vk::DescriptorPoolCreateInfo descriptor_pool_create_info{.maxSets = num_descriptor_sets,
|
||||
.poolSizeCount = static_cast<uint32_t>(pool_sizes.size()),
|
||||
.pPoolSizes = pool_sizes.data()};
|
||||
|
||||
descriptor_pool = get_device().get_handle().createDescriptorPool(descriptor_pool_create_info);
|
||||
}
|
||||
|
||||
void HPPOITDepthPeeling::create_gather_pass()
|
||||
{
|
||||
create_gather_pass_descriptor_set_layout();
|
||||
create_gather_pass_render_pass();
|
||||
create_gather_pass_depth_descriptor_sets();
|
||||
create_gather_pass_framebuffers(extent.width, extent.height);
|
||||
gatherPass.pipeline_layout = get_device().get_handle().createPipelineLayout({.setLayoutCount = 1, .pSetLayouts = &gatherPass.descriptor_set_layout});
|
||||
create_gather_pass_pipelines();
|
||||
}
|
||||
|
||||
void HPPOITDepthPeeling::create_gather_pass_depth_descriptor_sets()
|
||||
{
|
||||
vk::Device device = get_device().get_handle();
|
||||
for (auto &d : depths)
|
||||
{
|
||||
d.gather_descriptor_set = vkb::common::allocate_descriptor_set(device, descriptor_pool, gatherPass.descriptor_set_layout);
|
||||
}
|
||||
}
|
||||
|
||||
void HPPOITDepthPeeling::create_gather_pass_descriptor_set_layout()
|
||||
{
|
||||
std::array<vk::DescriptorSetLayoutBinding, 2> set_layout_bindings = {
|
||||
{{0, vk::DescriptorType::eUniformBuffer, 1, vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment},
|
||||
{1, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment}}};
|
||||
gatherPass.descriptor_set_layout = get_device().get_handle().createDescriptorSetLayout(
|
||||
{.bindingCount = static_cast<uint32_t>(set_layout_bindings.size()), .pBindings = set_layout_bindings.data()});
|
||||
}
|
||||
|
||||
void HPPOITDepthPeeling::create_gather_pass_framebuffers(const uint32_t width, const uint32_t height)
|
||||
{
|
||||
vk::Device device = get_device().get_handle();
|
||||
vk::FramebufferCreateInfo framebuffer_create_info{.width = width, .height = height, .layers = 1};
|
||||
for (uint32_t i = 0; i < kLayerMaxCount; ++i)
|
||||
{
|
||||
if (layers[i].gather_framebuffer)
|
||||
{
|
||||
device.destroyFramebuffer(layers[i].gather_framebuffer);
|
||||
}
|
||||
framebuffer_create_info.renderPass = gatherPass.render_pass;
|
||||
const std::array<vk::ImageView, 2> attachments{layers[i].image_view->get_handle(), depths[i % kDepthCount].image_view->get_handle()};
|
||||
framebuffer_create_info.setAttachments(attachments);
|
||||
layers[i].gather_framebuffer = device.createFramebuffer(framebuffer_create_info);
|
||||
}
|
||||
}
|
||||
|
||||
void HPPOITDepthPeeling::create_gather_pass_pipelines()
|
||||
{
|
||||
vk::Device device = get_device().get_handle();
|
||||
|
||||
std::vector<vk::PipelineShaderStageCreateInfo> shader_stages = {load_shader("oit_depth_peeling/gather.vert.spv", vk::ShaderStageFlagBits::eVertex),
|
||||
load_shader("oit_depth_peeling/gather_first.frag.spv", vk::ShaderStageFlagBits::eFragment)};
|
||||
|
||||
vk::VertexInputBindingDescription vertex_input_binding{0, sizeof(HPPVertex), vk::VertexInputRate::eVertex};
|
||||
std::array<vk::VertexInputAttributeDescription, 2> vertex_input_attributes = {{{0, 0, vk::Format::eR32G32B32Sfloat, offsetof(HPPVertex, pos)},
|
||||
{1, 0, vk::Format::eR32G32Sfloat, offsetof(HPPVertex, uv)}}};
|
||||
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()};
|
||||
|
||||
vk::PipelineColorBlendAttachmentState blend_attachment_state{.colorWriteMask = vk::FlagTraits<vk::ColorComponentFlagBits>::allFlags};
|
||||
|
||||
vk::PipelineDepthStencilStateCreateInfo depth_stencil_state{
|
||||
.depthTestEnable = true, .depthWriteEnable = true, .depthCompareOp = vk::CompareOp::eGreater, .back = {.compareOp = vk::CompareOp::eAlways}};
|
||||
|
||||
gatherPass.first_pipeline = vkb::common::create_graphics_pipeline(device,
|
||||
pipeline_cache,
|
||||
shader_stages,
|
||||
vertex_input_state,
|
||||
vk::PrimitiveTopology::eTriangleList,
|
||||
{},
|
||||
vk::PolygonMode::eFill,
|
||||
vk::CullModeFlagBits::eNone,
|
||||
vk::FrontFace::eCounterClockwise,
|
||||
{blend_attachment_state},
|
||||
depth_stencil_state,
|
||||
gatherPass.pipeline_layout,
|
||||
gatherPass.render_pass);
|
||||
|
||||
shader_stages[1] = load_shader("oit_depth_peeling/gather.frag.spv", vk::ShaderStageFlagBits::eFragment);
|
||||
|
||||
gatherPass.pipeline = vkb::common::create_graphics_pipeline(device,
|
||||
pipeline_cache,
|
||||
shader_stages,
|
||||
vertex_input_state,
|
||||
vk::PrimitiveTopology::eTriangleList,
|
||||
{},
|
||||
vk::PolygonMode::eFill,
|
||||
vk::CullModeFlagBits::eNone,
|
||||
vk::FrontFace::eCounterClockwise,
|
||||
{blend_attachment_state},
|
||||
depth_stencil_state,
|
||||
gatherPass.pipeline_layout,
|
||||
gatherPass.render_pass);
|
||||
}
|
||||
|
||||
void HPPOITDepthPeeling::create_gather_pass_render_pass()
|
||||
{
|
||||
std::array<vk::AttachmentDescription, 2> attachment_descriptions = {{{{},
|
||||
vk::Format::eR8G8B8A8Unorm,
|
||||
vk::SampleCountFlagBits::e1,
|
||||
vk::AttachmentLoadOp::eClear,
|
||||
vk::AttachmentStoreOp::eStore,
|
||||
vk::AttachmentLoadOp::eDontCare,
|
||||
vk::AttachmentStoreOp::eDontCare,
|
||||
vk::ImageLayout::eUndefined,
|
||||
vk::ImageLayout::eColorAttachmentOptimal},
|
||||
{{},
|
||||
vk::Format::eD32Sfloat,
|
||||
vk::SampleCountFlagBits::e1,
|
||||
vk::AttachmentLoadOp::eClear,
|
||||
vk::AttachmentStoreOp::eStore,
|
||||
vk::AttachmentLoadOp::eDontCare,
|
||||
vk::AttachmentStoreOp::eDontCare,
|
||||
vk::ImageLayout::eUndefined,
|
||||
vk::ImageLayout::eDepthStencilAttachmentOptimal}}};
|
||||
|
||||
vk::AttachmentReference color_attachment_reference{0, vk::ImageLayout::eColorAttachmentOptimal};
|
||||
vk::AttachmentReference depth_attachment_reference{1, vk::ImageLayout::eDepthStencilAttachmentOptimal};
|
||||
|
||||
vk::SubpassDescription subpass{.pipelineBindPoint = vk::PipelineBindPoint::eGraphics,
|
||||
.colorAttachmentCount = 1,
|
||||
.pColorAttachments = &color_attachment_reference,
|
||||
.pDepthStencilAttachment = &depth_attachment_reference};
|
||||
|
||||
vk::RenderPassCreateInfo render_pass_create_info{.attachmentCount = static_cast<uint32_t>(attachment_descriptions.size()),
|
||||
.pAttachments = attachment_descriptions.data(),
|
||||
.subpassCount = 1,
|
||||
.pSubpasses = &subpass};
|
||||
|
||||
gatherPass.render_pass = get_device().get_handle().createRenderPass(render_pass_create_info);
|
||||
}
|
||||
|
||||
void HPPOITDepthPeeling::create_images(const uint32_t width, const uint32_t height)
|
||||
{
|
||||
const vk::Extent3D image_extent = {width, height, 1};
|
||||
for (uint32_t i = 0; i < kLayerMaxCount; ++i)
|
||||
{
|
||||
layers[i].image = std::make_unique<vkb::core::HPPImage>(get_device(),
|
||||
image_extent,
|
||||
vk::Format::eR8G8B8A8Unorm,
|
||||
vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eColorAttachment,
|
||||
VMA_MEMORY_USAGE_GPU_ONLY,
|
||||
vk::SampleCountFlagBits::e1);
|
||||
layers[i].image_view = std::make_unique<vkb::core::HPPImageView>(*layers[i].image, vk::ImageViewType::e2D, vk::Format::eR8G8B8A8Unorm);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < kDepthCount; ++i)
|
||||
{
|
||||
depths[i].image = std::make_unique<vkb::core::HPPImage>(get_device(),
|
||||
image_extent,
|
||||
vk::Format::eD32Sfloat,
|
||||
vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eDepthStencilAttachment,
|
||||
VMA_MEMORY_USAGE_GPU_ONLY,
|
||||
vk::SampleCountFlagBits::e1);
|
||||
depths[i].image_view = std::make_unique<vkb::core::HPPImageView>(*depths[i].image, vk::ImageViewType::e2D, vk::Format::eD32Sfloat);
|
||||
}
|
||||
}
|
||||
|
||||
void HPPOITDepthPeeling::create_point_sampler()
|
||||
{
|
||||
point_sampler = vkb::common::create_sampler(
|
||||
get_device().get_handle(), vk::Filter::eNearest, vk::Filter::eNearest, vk::SamplerMipmapMode::eNearest, vk::SamplerAddressMode::eClampToEdge, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
void HPPOITDepthPeeling::create_scene_constants_buffer()
|
||||
{
|
||||
scene_constants =
|
||||
std::make_unique<vkb::core::BufferCpp>(get_device(), sizeof(SceneConstants), vk::BufferUsageFlagBits::eUniformBuffer, VMA_MEMORY_USAGE_CPU_TO_GPU);
|
||||
}
|
||||
|
||||
void HPPOITDepthPeeling::load_assets()
|
||||
{
|
||||
model = load_model("scenes/torusknot.gltf");
|
||||
background.texture = load_texture("textures/vulkan_logo_full.ktx", vkb::scene_graph::components::HPPImage::Color);
|
||||
}
|
||||
|
||||
void HPPOITDepthPeeling::prepare_camera()
|
||||
{
|
||||
camera.type = vkb::CameraType::LookAt;
|
||||
camera.set_position({0.0f, 0.0f, -4.0f});
|
||||
camera.set_rotation({0.0f, 0.0f, 0.0f});
|
||||
camera.set_perspective(60.0f, static_cast<float>(extent.width) / static_cast<float>(extent.height), 16.0f, 0.1f);
|
||||
}
|
||||
|
||||
void HPPOITDepthPeeling::update_descriptors()
|
||||
{
|
||||
vk::Device device = get_device().get_handle();
|
||||
|
||||
vk::DescriptorBufferInfo scene_constants_descriptor{scene_constants->get_handle(), 0, vk::WholeSize};
|
||||
|
||||
for (uint32_t i = 0; i < kDepthCount; ++i)
|
||||
{
|
||||
vk::DescriptorImageInfo depth_texture_descriptor{point_sampler,
|
||||
depths[(i + 1) % kDepthCount].image_view->get_handle(),
|
||||
vk::ImageLayout::eDepthStencilReadOnlyOptimal};
|
||||
|
||||
std::array<vk::WriteDescriptorSet, 2> write_descriptor_sets = {{{.dstSet = depths[i].gather_descriptor_set,
|
||||
.dstBinding = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = vk::DescriptorType::eUniformBuffer,
|
||||
.pBufferInfo = &scene_constants_descriptor},
|
||||
{.dstSet = depths[i].gather_descriptor_set,
|
||||
.dstBinding = 1,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
|
||||
.pImageInfo = &depth_texture_descriptor}}};
|
||||
device.updateDescriptorSets(write_descriptor_sets, {});
|
||||
}
|
||||
|
||||
vk::DescriptorImageInfo background_texture_descriptor{background.texture.sampler,
|
||||
background.texture.image->get_vk_image_view().get_handle(),
|
||||
vk::ImageLayout::eShaderReadOnlyOptimal};
|
||||
|
||||
std::array<vk::DescriptorImageInfo, kLayerMaxCount> layer_texture_descriptor;
|
||||
for (uint32_t i = 0; i < kLayerMaxCount; ++i)
|
||||
{
|
||||
layer_texture_descriptor[i].sampler = point_sampler;
|
||||
layer_texture_descriptor[i].imageView = layers[i].image_view->get_handle();
|
||||
layer_texture_descriptor[i].imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
|
||||
}
|
||||
|
||||
std::array<vk::WriteDescriptorSet, 3> write_descriptor_sets = {{{.dstSet = combinePass.descriptor_set,
|
||||
.dstBinding = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = vk::DescriptorType::eUniformBuffer,
|
||||
.pBufferInfo = &scene_constants_descriptor},
|
||||
{.dstSet = combinePass.descriptor_set,
|
||||
.dstBinding = 1,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
|
||||
.pImageInfo = &background_texture_descriptor},
|
||||
{.dstSet = combinePass.descriptor_set,
|
||||
.dstBinding = 2,
|
||||
.descriptorCount = static_cast<uint32_t>(layer_texture_descriptor.size()),
|
||||
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
|
||||
.pImageInfo = layer_texture_descriptor.data()}}};
|
||||
|
||||
device.updateDescriptorSets(write_descriptor_sets, {});
|
||||
}
|
||||
|
||||
void HPPOITDepthPeeling::update_scene_constants()
|
||||
{
|
||||
SceneConstants constants = {};
|
||||
constants.model_view_projection = camera.matrices.perspective * camera.matrices.view * glm::scale(glm::mat4(1.0f), glm::vec3(0.08));
|
||||
constants.background_grayscale = gui.background_grayscale;
|
||||
constants.object_opacity = gui.object_opacity;
|
||||
constants.front_layer_index = gui.layer_index_front;
|
||||
constants.back_layer_index = gui.layer_index_back;
|
||||
scene_constants->convert_and_update(constants);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::unique_ptr<vkb::VulkanSampleCpp> create_hpp_oit_depth_peeling()
|
||||
{
|
||||
return std::make_unique<HPPOITDepthPeeling>();
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
/* Copyright (c) 2024, Google
|
||||
* Copyright (c) 2024, NVIDIA
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hpp_api_vulkan_sample.h"
|
||||
|
||||
class HPPOITDepthPeeling : public HPPApiVulkanSample
|
||||
{
|
||||
public:
|
||||
HPPOITDepthPeeling();
|
||||
~HPPOITDepthPeeling();
|
||||
|
||||
private:
|
||||
// from vkb::Application
|
||||
bool prepare(const vkb::ApplicationOptions &options) override;
|
||||
bool resize(const uint32_t width, const uint32_t height) override;
|
||||
|
||||
// from vkb::VulkanSample
|
||||
void request_gpu_features(vkb::core::HPPPhysicalDevice &gpu) override;
|
||||
|
||||
// from HPPApiVulkanSample
|
||||
void build_command_buffers() override;
|
||||
void on_update_ui_overlay(vkb::Drawer &drawer) override;
|
||||
void render(float delta_time) override;
|
||||
|
||||
void create_background_pipeline();
|
||||
void create_combine_pass();
|
||||
void create_combine_pass_pipeline();
|
||||
void create_descriptor_pool();
|
||||
void create_gather_pass();
|
||||
void create_gather_pass_depth_descriptor_sets();
|
||||
void create_gather_pass_descriptor_set_layout();
|
||||
void create_gather_pass_framebuffers(const uint32_t width, const uint32_t height);
|
||||
void create_gather_pass_pipelines();
|
||||
void create_gather_pass_render_pass();
|
||||
void create_images(const uint32_t width, const uint32_t height);
|
||||
void create_point_sampler();
|
||||
void create_scene_constants_buffer();
|
||||
void load_assets();
|
||||
void prepare_camera();
|
||||
void update_descriptors();
|
||||
void update_scene_constants();
|
||||
|
||||
private:
|
||||
static constexpr uint32_t kLayerMaxCount = 8;
|
||||
|
||||
static constexpr uint32_t kDepthCount = 2;
|
||||
|
||||
static constexpr float kBackgroundGrayscaleMin = 0.0f;
|
||||
static constexpr float kBackgroundGrayscaleMax = 1.0f;
|
||||
|
||||
static constexpr float kObjectAlphaMin = 0.0f;
|
||||
static constexpr float kObjectAlphaMax = 1.0f;
|
||||
|
||||
struct Background
|
||||
{
|
||||
vk::Pipeline pipeline = {};
|
||||
HPPTexture texture = {};
|
||||
|
||||
void destroy(vk::Device device)
|
||||
{
|
||||
device.destroyPipeline(pipeline);
|
||||
device.destroySampler(texture.sampler);
|
||||
}
|
||||
};
|
||||
|
||||
struct CombinePass
|
||||
{
|
||||
vk::DescriptorSet descriptor_set = {};
|
||||
vk::DescriptorSetLayout descriptor_set_layout = {};
|
||||
vk::Pipeline pipeline = {};
|
||||
vk::PipelineLayout pipeline_layout = {};
|
||||
|
||||
void destroy(vk::Device device)
|
||||
{
|
||||
// descriptor_set is implicitly destroyed when the managing descriptor_pool is destroyed
|
||||
device.destroyDescriptorSetLayout(descriptor_set_layout);
|
||||
device.destroyPipeline(pipeline);
|
||||
device.destroyPipelineLayout(pipeline_layout);
|
||||
|
||||
descriptor_set_layout = nullptr;
|
||||
pipeline = nullptr;
|
||||
pipeline_layout = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
struct Depth
|
||||
{
|
||||
vk::DescriptorSet gather_descriptor_set = {};
|
||||
std::unique_ptr<vkb::core::HPPImage> image = {};
|
||||
std::unique_ptr<vkb::core::HPPImageView> image_view = {};
|
||||
|
||||
void destroy()
|
||||
{
|
||||
image_view.reset();
|
||||
image.reset();
|
||||
}
|
||||
};
|
||||
|
||||
struct GatherPass
|
||||
{
|
||||
vk::DescriptorSetLayout descriptor_set_layout = {};
|
||||
vk::Pipeline first_pipeline = {};
|
||||
vk::Pipeline pipeline = {};
|
||||
vk::PipelineLayout pipeline_layout = {};
|
||||
vk::RenderPass render_pass = {};
|
||||
|
||||
void destroy(vk::Device device)
|
||||
{
|
||||
device.destroyDescriptorSetLayout(descriptor_set_layout);
|
||||
device.destroyPipeline(first_pipeline);
|
||||
device.destroyPipeline(pipeline);
|
||||
device.destroyPipelineLayout(pipeline_layout);
|
||||
device.destroyRenderPass(render_pass);
|
||||
|
||||
descriptor_set_layout = nullptr;
|
||||
first_pipeline = nullptr;
|
||||
pipeline = nullptr;
|
||||
pipeline_layout = nullptr;
|
||||
render_pass = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
struct GUI
|
||||
{
|
||||
float background_grayscale = 0.3f;
|
||||
int32_t camera_auto_rotation = false;
|
||||
int32_t layer_index_back = kLayerMaxCount - 1;
|
||||
int32_t layer_index_front = 0;
|
||||
float object_opacity = 0.5f;
|
||||
};
|
||||
|
||||
struct Layer
|
||||
{
|
||||
vk::Framebuffer gather_framebuffer = {};
|
||||
std::unique_ptr<vkb::core::HPPImage> image = {};
|
||||
std::unique_ptr<vkb::core::HPPImageView> image_view = {};
|
||||
|
||||
void destroy(vk::Device device)
|
||||
{
|
||||
device.destroyFramebuffer(gather_framebuffer);
|
||||
image_view.reset();
|
||||
image.reset();
|
||||
|
||||
gather_framebuffer = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
struct SceneConstants
|
||||
{
|
||||
glm::mat4 model_view_projection = {};
|
||||
glm::f32 background_grayscale = {};
|
||||
glm::f32 object_opacity = {};
|
||||
glm::int32 front_layer_index = {};
|
||||
glm::int32 back_layer_index = {};
|
||||
};
|
||||
|
||||
private:
|
||||
Background background = {};
|
||||
CombinePass combinePass = {};
|
||||
std::array<Depth, kDepthCount> depths = {};
|
||||
vk::DescriptorPool descriptor_pool = {};
|
||||
GatherPass gatherPass = {};
|
||||
GUI gui = {};
|
||||
std::array<Layer, kLayerMaxCount> layers = {};
|
||||
std::unique_ptr<vkb::scene_graph::components::HPPSubMesh> model = {};
|
||||
vk::Sampler point_sampler = {};
|
||||
std::unique_ptr<vkb::core::BufferCpp> scene_constants = {};
|
||||
};
|
||||
|
||||
std::unique_ptr<vkb::VulkanSampleCpp> create_hpp_oit_depth_peeling();
|
||||
Reference in New Issue
Block a user