2023-05-13 12:22:04 +02:00
/*
* Vulkan Example - Hardware accelerated ray tracing intersection shader samples
*
2024-05-04 14:06:18 +02:00
* Copyright ( C ) 2023 - 2024 by Sascha Willems - www . saschawillems . de
2023-05-13 12:22:04 +02:00
*
* This sample uses intersection shaders for doing prodcedural ray traced geometry
* Instead of passing actual geometry , this samples only passes bounding boxes and sphere descriptions
* The bounding boxes are used for the ray traversal and the sphere intersections are done
* within the intersection shader
*
* This code is licensed under the MIT license ( MIT ) ( http : //opensource.org/licenses/MIT)
*/
# include "VulkanRaytracingSample.h"
class VulkanExample : public VulkanRaytracingSample
{
public :
AccelerationStructure bottomLevelAS ;
AccelerationStructure topLevelAS ;
std : : vector < VkRayTracingShaderGroupCreateInfoKHR > shaderGroups { } ;
struct ShaderBindingTables {
ShaderBindingTable raygen ;
ShaderBindingTable miss ;
ShaderBindingTable hit ;
} shaderBindingTables ;
struct UniformData {
glm : : mat4 viewInverse ;
glm : : mat4 projInverse ;
glm : : vec4 lightPos ;
} uniformData ;
vks : : Buffer ubo ;
VkPipeline pipeline ;
VkPipelineLayout pipelineLayout ;
VkDescriptorSet descriptorSet ;
VkDescriptorSetLayout descriptorSetLayout ;
struct Sphere {
glm : : vec3 center ;
float radius ;
glm : : vec4 color ;
} ;
struct AABB {
glm : : vec3 min ;
glm : : vec3 max ;
} ;
vks : : Buffer spheresBuffer ;
vks : : Buffer aabbsBuffer ;
uint32_t aabbCount { 0 } ;
// This sample is derived from an extended base class that saves most of the ray tracing setup boiler plate
VulkanExample ( ) : VulkanRaytracingSample ( )
{
title = " Ray tracing intersection shaders " ;
timerSpeed * = 0.25f ;
camera . type = Camera : : CameraType : : lookat ;
camera . setPerspective ( 60.0f , ( float ) width / ( float ) height , 0.1f , 512.0f ) ;
camera . setRotation ( glm : : vec3 ( 0.0f , 0.0f , 0.0f ) ) ;
2023-05-13 16:19:40 +02:00
camera . setTranslation ( glm : : vec3 ( 0.0f , 0.0f , - 60.0f ) ) ;
2023-05-13 12:22:04 +02:00
enableExtensions ( ) ;
}
~ VulkanExample ( )
{
vkDestroyPipeline ( device , pipeline , nullptr ) ;
vkDestroyPipelineLayout ( device , pipelineLayout , nullptr ) ;
vkDestroyDescriptorSetLayout ( device , descriptorSetLayout , nullptr ) ;
deleteStorageImage ( ) ;
deleteAccelerationStructure ( bottomLevelAS ) ;
deleteAccelerationStructure ( topLevelAS ) ;
shaderBindingTables . raygen . destroy ( ) ;
shaderBindingTables . miss . destroy ( ) ;
shaderBindingTables . hit . destroy ( ) ;
ubo . destroy ( ) ;
2023-08-05 13:32:17 +02:00
spheresBuffer . destroy ( ) ;
aabbsBuffer . destroy ( ) ;
2023-05-13 12:22:04 +02:00
}
void createBuffers ( )
{
// We'll be using two buffers to describe the procedural geometry
2023-05-13 16:19:40 +02:00
// A buffer with randpmly generatd sphere descriptions (center, radius, material) that'll be passed to the ray tracing shaders as a shader storage buffer object
2023-05-13 12:22:04 +02:00
std : : vector < Sphere > spheres { } ;
2023-05-13 16:19:40 +02:00
std : : default_random_engine rndGenerator ( benchmark . active ? 0 : ( unsigned ) time ( nullptr ) ) ;
std : : uniform_real_distribution < float > uniformDist ( 0.0 , 1.0 ) ;
std : : uniform_real_distribution < float > sizeDist ( 1.0 , 2.0 ) ;
for ( uint32_t i = 0 ; i < 1024 ; i + + ) {
Sphere sphere { } ;
//sphere.center =
sphere . radius = sizeDist ( rndGenerator ) ;
sphere . color = glm : : vec4 ( uniformDist ( rndGenerator ) , uniformDist ( rndGenerator ) , uniformDist ( rndGenerator ) , 1.0f ) ;
// Get a random point in a sphere
float x , y , z , d { 0.0f } ;
do {
x = uniformDist ( rndGenerator ) * 2.0f - 1.0f ;
y = uniformDist ( rndGenerator ) * 2.0f - 1.0f ;
z = uniformDist ( rndGenerator ) * 2.0f - 1.0f ;
d = x * x + y * y + z * z ;
} while ( d > 1.0 ) ;
sphere . center = glm : : vec3 ( x , y , z ) * 25.0f ;
spheres . push_back ( sphere ) ;
}
2023-05-13 12:22:04 +02:00
// A buffer with the (axis aligned) bounding boxes of our sphere, which is used during the ray tracing traversal for hit detection
std : : vector < AABB > aabbs { } ;
for ( auto & sphere : spheres ) {
aabbs . push_back ( { sphere . center - glm : : vec3 ( sphere . radius ) , sphere . center + glm : : vec3 ( sphere . radius ) } ) ;
}
aabbCount = static_cast < uint32_t > ( aabbs . size ( ) ) ;
// Copy the buffer to the device for performance reasons
vks : : Buffer stagingBuffer { } ;
VkBufferUsageFlags usageFlags = VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT ;
// Spheres
VK_CHECK_RESULT ( vulkanDevice - > createBuffer ( VK_BUFFER_USAGE_TRANSFER_SRC_BIT , VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT , & stagingBuffer , sizeof ( Sphere ) * spheres . size ( ) , spheres . data ( ) ) ) ;
2023-07-16 14:06:18 +02:00
VK_CHECK_RESULT ( vulkanDevice - > createBuffer ( usageFlags , VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT , & spheresBuffer , sizeof ( Sphere ) * spheres . size ( ) ) ) ;
2023-05-13 12:22:04 +02:00
vulkanDevice - > copyBuffer ( & stagingBuffer , & spheresBuffer , queue ) ;
stagingBuffer . destroy ( ) ;
// AABBs
VK_CHECK_RESULT ( vulkanDevice - > createBuffer ( VK_BUFFER_USAGE_TRANSFER_SRC_BIT , VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT , & stagingBuffer , sizeof ( AABB ) * aabbs . size ( ) , aabbs . data ( ) ) ) ;
2023-07-16 14:06:18 +02:00
VK_CHECK_RESULT ( vulkanDevice - > createBuffer ( usageFlags , VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT , & aabbsBuffer , sizeof ( AABB ) * aabbs . size ( ) ) ) ;
2023-05-13 12:22:04 +02:00
vulkanDevice - > copyBuffer ( & stagingBuffer , & aabbsBuffer , queue ) ;
stagingBuffer . destroy ( ) ;
}
/*
Create the bottom level acceleration structure only containing axis aligned bounding boxes for our procedural geometry
*/
void createBottomLevelAccelerationStructure ( )
{
// Build
VkAccelerationStructureGeometryKHR accelerationStructureGeometry = vks : : initializers : : accelerationStructureGeometryKHR ( ) ;
accelerationStructureGeometry . flags = VK_GEOMETRY_OPAQUE_BIT_KHR ;
// Instead of providing actual geometry (e.g. triangles), we only provide the axis aligned bounding boxes (AABBs) of the spheres
// The data for the actual spheres is passed elsewhere as a shader storage buffer object
accelerationStructureGeometry . geometryType = VK_GEOMETRY_TYPE_AABBS_KHR ;
accelerationStructureGeometry . geometry . aabbs . sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_AABBS_DATA_KHR ;
accelerationStructureGeometry . geometry . aabbs . data . deviceAddress = getBufferDeviceAddress ( aabbsBuffer . buffer ) ;
accelerationStructureGeometry . geometry . aabbs . stride = sizeof ( AABB ) ;
// Get size info
VkAccelerationStructureBuildGeometryInfoKHR accelerationStructureBuildGeometryInfo = vks : : initializers : : accelerationStructureBuildGeometryInfoKHR ( ) ;
accelerationStructureBuildGeometryInfo . type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR ;
accelerationStructureBuildGeometryInfo . flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR ;
accelerationStructureBuildGeometryInfo . geometryCount = 1 ;
accelerationStructureBuildGeometryInfo . pGeometries = & accelerationStructureGeometry ;
VkAccelerationStructureBuildSizesInfoKHR accelerationStructureBuildSizesInfo = vks : : initializers : : accelerationStructureBuildSizesInfoKHR ( ) ;
vkGetAccelerationStructureBuildSizesKHR (
device ,
VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR ,
& accelerationStructureBuildGeometryInfo ,
& aabbCount ,
& accelerationStructureBuildSizesInfo ) ;
createAccelerationStructure ( bottomLevelAS , VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR , accelerationStructureBuildSizesInfo ) ;
// Create a small scratch buffer used during build of the bottom level acceleration structure
ScratchBuffer scratchBuffer = createScratchBuffer ( accelerationStructureBuildSizesInfo . buildScratchSize ) ;
VkAccelerationStructureBuildGeometryInfoKHR accelerationBuildGeometryInfo = vks : : initializers : : accelerationStructureBuildGeometryInfoKHR ( ) ;
accelerationBuildGeometryInfo . type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR ;
accelerationBuildGeometryInfo . flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR ;
accelerationBuildGeometryInfo . mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR ;
accelerationBuildGeometryInfo . dstAccelerationStructure = bottomLevelAS . handle ;
accelerationBuildGeometryInfo . geometryCount = 1 ;
accelerationBuildGeometryInfo . pGeometries = & accelerationStructureGeometry ;
accelerationBuildGeometryInfo . scratchData . deviceAddress = scratchBuffer . deviceAddress ;
VkAccelerationStructureBuildRangeInfoKHR accelerationStructureBuildRangeInfo { } ;
accelerationStructureBuildRangeInfo . primitiveCount = aabbCount ;
std : : vector < VkAccelerationStructureBuildRangeInfoKHR * > accelerationBuildStructureRangeInfos = { & accelerationStructureBuildRangeInfo } ;
// Build the acceleration structure on the device via a one-time command buffer submission
// Some implementations may support acceleration structure building on the host (VkPhysicalDeviceAccelerationStructureFeaturesKHR->accelerationStructureHostCommands), but we prefer device builds
VkCommandBuffer commandBuffer = vulkanDevice - > createCommandBuffer ( VK_COMMAND_BUFFER_LEVEL_PRIMARY , true ) ;
vkCmdBuildAccelerationStructuresKHR (
commandBuffer ,
1 ,
& accelerationBuildGeometryInfo ,
accelerationBuildStructureRangeInfos . data ( ) ) ;
vulkanDevice - > flushCommandBuffer ( commandBuffer , queue ) ;
deleteScratchBuffer ( scratchBuffer ) ;
}
/*
The top level acceleration structure contains the scene ' s object instances
*/
void createTopLevelAccelerationStructure ( )
{
VkTransformMatrixKHR transformMatrix = {
1.0f , 0.0f , 0.0f , 0.0f ,
0.0f , 1.0f , 0.0f , 0.0f ,
0.0f , 0.0f , 1.0f , 0.0f } ;
VkAccelerationStructureInstanceKHR instance { } ;
instance . transform = transformMatrix ;
instance . instanceCustomIndex = 0 ;
instance . mask = 0xFF ;
instance . instanceShaderBindingTableRecordOffset = 0 ;
instance . flags = VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR ;
instance . accelerationStructureReference = bottomLevelAS . deviceAddress ;
// Buffer for instance data
vks : : Buffer instancesBuffer ;
VK_CHECK_RESULT ( vulkanDevice - > createBuffer (
VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR ,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ,
& instancesBuffer ,
sizeof ( VkAccelerationStructureInstanceKHR ) ,
& instance ) ) ;
VkDeviceOrHostAddressConstKHR instanceDataDeviceAddress { } ;
instanceDataDeviceAddress . deviceAddress = getBufferDeviceAddress ( instancesBuffer . buffer ) ;
VkAccelerationStructureGeometryKHR accelerationStructureGeometry = vks : : initializers : : accelerationStructureGeometryKHR ( ) ;
accelerationStructureGeometry . geometryType = VK_GEOMETRY_TYPE_INSTANCES_KHR ;
accelerationStructureGeometry . flags = VK_GEOMETRY_OPAQUE_BIT_KHR ;
accelerationStructureGeometry . geometry . instances . sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_INSTANCES_DATA_KHR ;
accelerationStructureGeometry . geometry . instances . arrayOfPointers = VK_FALSE ;
accelerationStructureGeometry . geometry . instances . data = instanceDataDeviceAddress ;
// Get size info
VkAccelerationStructureBuildGeometryInfoKHR accelerationStructureBuildGeometryInfo = vks : : initializers : : accelerationStructureBuildGeometryInfoKHR ( ) ;
accelerationStructureBuildGeometryInfo . type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR ;
accelerationStructureBuildGeometryInfo . flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR ;
accelerationStructureBuildGeometryInfo . geometryCount = 1 ;
accelerationStructureBuildGeometryInfo . pGeometries = & accelerationStructureGeometry ;
uint32_t primitive_count = 1 ;
VkAccelerationStructureBuildSizesInfoKHR accelerationStructureBuildSizesInfo = vks : : initializers : : accelerationStructureBuildSizesInfoKHR ( ) ;
vkGetAccelerationStructureBuildSizesKHR (
device ,
VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR ,
& accelerationStructureBuildGeometryInfo ,
& primitive_count ,
& accelerationStructureBuildSizesInfo ) ;
createAccelerationStructure ( topLevelAS , VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR , accelerationStructureBuildSizesInfo ) ;
// Create a small scratch buffer used during build of the top level acceleration structure
ScratchBuffer scratchBuffer = createScratchBuffer ( accelerationStructureBuildSizesInfo . buildScratchSize ) ;
VkAccelerationStructureBuildGeometryInfoKHR accelerationBuildGeometryInfo = vks : : initializers : : accelerationStructureBuildGeometryInfoKHR ( ) ;
accelerationBuildGeometryInfo . type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR ;
accelerationBuildGeometryInfo . flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR ;
accelerationBuildGeometryInfo . mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR ;
accelerationBuildGeometryInfo . dstAccelerationStructure = topLevelAS . handle ;
accelerationBuildGeometryInfo . geometryCount = 1 ;
accelerationBuildGeometryInfo . pGeometries = & accelerationStructureGeometry ;
accelerationBuildGeometryInfo . scratchData . deviceAddress = scratchBuffer . deviceAddress ;
VkAccelerationStructureBuildRangeInfoKHR accelerationStructureBuildRangeInfo { } ;
accelerationStructureBuildRangeInfo . primitiveCount = 1 ;
accelerationStructureBuildRangeInfo . primitiveOffset = 0 ;
accelerationStructureBuildRangeInfo . firstVertex = 0 ;
accelerationStructureBuildRangeInfo . transformOffset = 0 ;
std : : vector < VkAccelerationStructureBuildRangeInfoKHR * > accelerationBuildStructureRangeInfos = { & accelerationStructureBuildRangeInfo } ;
// Build the acceleration structure on the device via a one-time command buffer submission
// Some implementations may support acceleration structure building on the host (VkPhysicalDeviceAccelerationStructureFeaturesKHR->accelerationStructureHostCommands), but we prefer device builds
VkCommandBuffer commandBuffer = vulkanDevice - > createCommandBuffer ( VK_COMMAND_BUFFER_LEVEL_PRIMARY , true ) ;
vkCmdBuildAccelerationStructuresKHR (
commandBuffer ,
1 ,
& accelerationBuildGeometryInfo ,
accelerationBuildStructureRangeInfos . data ( ) ) ;
vulkanDevice - > flushCommandBuffer ( commandBuffer , queue ) ;
deleteScratchBuffer ( scratchBuffer ) ;
instancesBuffer . destroy ( ) ;
}
/*
Create the Shader Binding Tables that binds the programs and top - level acceleration structure
SBT Layout used in this sample :
/ - - - - - - - - - - - \
| raygen |
| - - - - - - - - - - - |
| miss |
| - - - - - - - - - - - |
| hit + int |
\ - - - - - - - - - - - /
*/
void createShaderBindingTables ( ) {
const uint32_t handleSize = rayTracingPipelineProperties . shaderGroupHandleSize ;
const uint32_t handleSizeAligned = vks : : tools : : alignedSize ( rayTracingPipelineProperties . shaderGroupHandleSize , rayTracingPipelineProperties . shaderGroupHandleAlignment ) ;
const uint32_t groupCount = static_cast < uint32_t > ( shaderGroups . size ( ) ) ;
const uint32_t sbtSize = groupCount * handleSizeAligned ;
std : : vector < uint8_t > shaderHandleStorage ( sbtSize ) ;
VK_CHECK_RESULT ( vkGetRayTracingShaderGroupHandlesKHR ( device , pipeline , 0 , groupCount , sbtSize , shaderHandleStorage . data ( ) ) ) ;
createShaderBindingTable ( shaderBindingTables . raygen , 1 ) ;
createShaderBindingTable ( shaderBindingTables . miss , 1 ) ;
createShaderBindingTable ( shaderBindingTables . hit , 1 ) ;
// Copy handles
memcpy ( shaderBindingTables . raygen . mapped , shaderHandleStorage . data ( ) , handleSize ) ;
memcpy ( shaderBindingTables . miss . mapped , shaderHandleStorage . data ( ) + handleSizeAligned , handleSize ) ;
memcpy ( shaderBindingTables . hit . mapped , shaderHandleStorage . data ( ) + handleSizeAligned * 2 , handleSize ) ;
}
/*
Create the descriptor sets used for the ray tracing dispatch
*/
void createDescriptorSets ( )
{
std : : vector < VkDescriptorPoolSize > poolSizes = {
{ VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR , 1 } ,
{ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE , 1 } ,
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 1 } ,
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER , 2 }
} ;
VkDescriptorPoolCreateInfo descriptorPoolCreateInfo = vks : : initializers : : descriptorPoolCreateInfo ( poolSizes , 1 ) ;
VK_CHECK_RESULT ( vkCreateDescriptorPool ( device , & descriptorPoolCreateInfo , nullptr , & descriptorPool ) ) ;
VkDescriptorSetAllocateInfo descriptorSetAllocateInfo = vks : : initializers : : descriptorSetAllocateInfo ( descriptorPool , & descriptorSetLayout , 1 ) ;
VK_CHECK_RESULT ( vkAllocateDescriptorSets ( device , & descriptorSetAllocateInfo , & descriptorSet ) ) ;
VkWriteDescriptorSetAccelerationStructureKHR descriptorAccelerationStructureInfo = vks : : initializers : : writeDescriptorSetAccelerationStructureKHR ( ) ;
descriptorAccelerationStructureInfo . accelerationStructureCount = 1 ;
descriptorAccelerationStructureInfo . pAccelerationStructures = & topLevelAS . handle ;
VkWriteDescriptorSet accelerationStructureWrite { } ;
accelerationStructureWrite . sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET ;
// The specialized acceleration structure descriptor has to be chained
accelerationStructureWrite . pNext = & descriptorAccelerationStructureInfo ;
accelerationStructureWrite . dstSet = descriptorSet ;
accelerationStructureWrite . dstBinding = 0 ;
accelerationStructureWrite . descriptorCount = 1 ;
accelerationStructureWrite . descriptorType = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR ;
// We pass the sphere descriptions as shader storage buffer, so the ray tracing shaders can source properties from it
VkDescriptorImageInfo storageImageDescriptor { VK_NULL_HANDLE , storageImage . view , VK_IMAGE_LAYOUT_GENERAL } ;
VkDescriptorBufferInfo spheresBufferDescriptor { spheresBuffer . buffer , 0 , VK_WHOLE_SIZE } ;
std : : vector < VkWriteDescriptorSet > writeDescriptorSets = {
// Binding 0: Top level acceleration structure
accelerationStructureWrite ,
// Binding 1: Ray tracing result image
vks : : initializers : : writeDescriptorSet ( descriptorSet , VK_DESCRIPTOR_TYPE_STORAGE_IMAGE , 1 , & storageImageDescriptor ) ,
// Binding 2: Uniform data
vks : : initializers : : writeDescriptorSet ( descriptorSet , VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 2 , & ubo . descriptor ) ,
// Binding 3: Spheres buffer
vks : : initializers : : writeDescriptorSet ( descriptorSet , VK_DESCRIPTOR_TYPE_STORAGE_BUFFER , 3 , & spheresBufferDescriptor ) ,
} ;
vkUpdateDescriptorSets ( device , static_cast < uint32_t > ( writeDescriptorSets . size ( ) ) , writeDescriptorSets . data ( ) , 0 , VK_NULL_HANDLE ) ;
}
/*
Create our ray tracing pipeline
*/
void createRayTracingPipeline ( )
{
std : : vector < VkDescriptorSetLayoutBinding > setLayoutBindings = {
// Binding 0: Acceleration structure
vks : : initializers : : descriptorSetLayoutBinding ( VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR , VK_SHADER_STAGE_RAYGEN_BIT_KHR | VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR , 0 ) ,
// Binding 1: Storage image
vks : : initializers : : descriptorSetLayoutBinding ( VK_DESCRIPTOR_TYPE_STORAGE_IMAGE , VK_SHADER_STAGE_RAYGEN_BIT_KHR , 1 ) ,
// Binding 2: Uniform buffer
vks : : initializers : : descriptorSetLayoutBinding ( VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , VK_SHADER_STAGE_RAYGEN_BIT_KHR | VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR | VK_SHADER_STAGE_MISS_BIT_KHR | VK_SHADER_STAGE_INTERSECTION_BIT_KHR , 2 ) ,
// Binding 3: Spheres buffer
vks : : initializers : : descriptorSetLayoutBinding ( VK_DESCRIPTOR_TYPE_STORAGE_BUFFER , VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR | VK_SHADER_STAGE_INTERSECTION_BIT_KHR , 3 ) ,
} ;
VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCI = vks : : initializers : : descriptorSetLayoutCreateInfo ( setLayoutBindings ) ;
VK_CHECK_RESULT ( vkCreateDescriptorSetLayout ( device , & descriptorSetLayoutCI , nullptr , & descriptorSetLayout ) ) ;
VkPipelineLayoutCreateInfo pPipelineLayoutCI = vks : : initializers : : pipelineLayoutCreateInfo ( & descriptorSetLayout , 1 ) ;
VK_CHECK_RESULT ( vkCreatePipelineLayout ( device , & pPipelineLayoutCI , nullptr , & pipelineLayout ) ) ;
/*
Setup ray tracing shader groups
*/
std : : vector < VkPipelineShaderStageCreateInfo > shaderStages ;
// Ray generation group
{
shaderStages . push_back ( loadShader ( getShadersPath ( ) + " raytracingintersection/raygen.rgen.spv " , VK_SHADER_STAGE_RAYGEN_BIT_KHR ) ) ;
VkRayTracingShaderGroupCreateInfoKHR shaderGroup { } ;
shaderGroup . sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR ;
shaderGroup . type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR ;
shaderGroup . generalShader = static_cast < uint32_t > ( shaderStages . size ( ) ) - 1 ;
shaderGroup . closestHitShader = VK_SHADER_UNUSED_KHR ;
shaderGroup . anyHitShader = VK_SHADER_UNUSED_KHR ;
shaderGroup . intersectionShader = VK_SHADER_UNUSED_KHR ;
shaderGroups . push_back ( shaderGroup ) ;
}
// Miss group
{
shaderStages . push_back ( loadShader ( getShadersPath ( ) + " raytracingintersection/miss.rmiss.spv " , VK_SHADER_STAGE_MISS_BIT_KHR ) ) ;
VkRayTracingShaderGroupCreateInfoKHR shaderGroup { } ;
shaderGroup . sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR ;
shaderGroup . type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR ;
shaderGroup . generalShader = static_cast < uint32_t > ( shaderStages . size ( ) ) - 1 ;
shaderGroup . closestHitShader = VK_SHADER_UNUSED_KHR ;
shaderGroup . anyHitShader = VK_SHADER_UNUSED_KHR ;
shaderGroup . intersectionShader = VK_SHADER_UNUSED_KHR ;
shaderGroups . push_back ( shaderGroup ) ;
}
// Closest hit group (procedural)
{
shaderStages . push_back ( loadShader ( getShadersPath ( ) + " raytracingintersection/closesthit.rchit.spv " , VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR ) ) ;
VkRayTracingShaderGroupCreateInfoKHR shaderGroup { } ;
shaderGroup . sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR ;
shaderGroup . type = VK_RAY_TRACING_SHADER_GROUP_TYPE_PROCEDURAL_HIT_GROUP_KHR ;
shaderGroup . generalShader = VK_SHADER_UNUSED_KHR ;
shaderGroup . closestHitShader = static_cast < uint32_t > ( shaderStages . size ( ) ) - 1 ;
shaderGroup . anyHitShader = VK_SHADER_UNUSED_KHR ;
// This group als uses an intersection shader for proedural geometry (see interseciton.rint for details)
shaderStages . push_back ( loadShader ( getShadersPath ( ) + " raytracingintersection/intersection.rint.spv " , VK_SHADER_STAGE_INTERSECTION_BIT_KHR ) ) ;
shaderGroup . intersectionShader = static_cast < uint32_t > ( shaderStages . size ( ) ) - 1 ;
shaderGroups . push_back ( shaderGroup ) ;
}
VkRayTracingPipelineCreateInfoKHR rayTracingPipelineCI = vks : : initializers : : rayTracingPipelineCreateInfoKHR ( ) ;
rayTracingPipelineCI . stageCount = static_cast < uint32_t > ( shaderStages . size ( ) ) ;
rayTracingPipelineCI . pStages = shaderStages . data ( ) ;
rayTracingPipelineCI . groupCount = static_cast < uint32_t > ( shaderGroups . size ( ) ) ;
rayTracingPipelineCI . pGroups = shaderGroups . data ( ) ;
2024-05-04 14:06:18 +02:00
rayTracingPipelineCI . maxPipelineRayRecursionDepth = std : : min ( uint32_t ( 2 ) , rayTracingPipelineProperties . maxRayRecursionDepth ) ;
2023-05-13 12:22:04 +02:00
rayTracingPipelineCI . layout = pipelineLayout ;
VK_CHECK_RESULT ( vkCreateRayTracingPipelinesKHR ( device , VK_NULL_HANDLE , VK_NULL_HANDLE , 1 , & rayTracingPipelineCI , nullptr , & pipeline ) ) ;
}
/*
Create the uniform buffer used to pass matrices to the ray tracing ray generation shader
*/
void createUniformBuffer ( )
{
VK_CHECK_RESULT ( vulkanDevice - > createBuffer (
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT ,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ,
& ubo ,
sizeof ( uniformData ) ,
& uniformData ) ) ;
VK_CHECK_RESULT ( ubo . map ( ) ) ;
updateUniformBuffers ( ) ;
}
/*
If the window has been resized , we need to recreate the storage image and it ' s descriptor
*/
void handleResize ( )
{
// Recreate image
createStorageImage ( swapChain . colorFormat , { width , height , 1 } ) ;
// Update descriptor
VkDescriptorImageInfo storageImageDescriptor { VK_NULL_HANDLE , storageImage . view , VK_IMAGE_LAYOUT_GENERAL } ;
VkWriteDescriptorSet resultImageWrite = vks : : initializers : : writeDescriptorSet ( descriptorSet , VK_DESCRIPTOR_TYPE_STORAGE_IMAGE , 1 , & storageImageDescriptor ) ;
vkUpdateDescriptorSets ( device , 1 , & resultImageWrite , 0 , VK_NULL_HANDLE ) ;
resized = false ;
}
/*
Command buffer generation
*/
void buildCommandBuffers ( )
{
if ( resized )
{
handleResize ( ) ;
}
VkCommandBufferBeginInfo cmdBufInfo = vks : : initializers : : commandBufferBeginInfo ( ) ;
VkImageSubresourceRange subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT , 0 , 1 , 0 , 1 } ;
for ( int32_t i = 0 ; i < drawCmdBuffers . size ( ) ; + + i )
{
VK_CHECK_RESULT ( vkBeginCommandBuffer ( drawCmdBuffers [ i ] , & cmdBufInfo ) ) ;
/*
Dispatch the ray tracing commands
*/
vkCmdBindPipeline ( drawCmdBuffers [ i ] , VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR , pipeline ) ;
vkCmdBindDescriptorSets ( drawCmdBuffers [ i ] , VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR , pipelineLayout , 0 , 1 , & descriptorSet , 0 , 0 ) ;
VkStridedDeviceAddressRegionKHR emptySbtEntry = { } ;
vkCmdTraceRaysKHR (
drawCmdBuffers [ i ] ,
& shaderBindingTables . raygen . stridedDeviceAddressRegion ,
& shaderBindingTables . miss . stridedDeviceAddressRegion ,
& shaderBindingTables . hit . stridedDeviceAddressRegion ,
& emptySbtEntry ,
width ,
height ,
1 ) ;
/*
Copy ray tracing output to swap chain image
*/
// Prepare current swap chain image as transfer destination
vks : : tools : : setImageLayout (
drawCmdBuffers [ i ] ,
swapChain . images [ i ] ,
VK_IMAGE_LAYOUT_UNDEFINED ,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ,
subresourceRange ) ;
// Prepare ray tracing output image as transfer source
vks : : tools : : setImageLayout (
drawCmdBuffers [ i ] ,
storageImage . image ,
VK_IMAGE_LAYOUT_GENERAL ,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL ,
subresourceRange ) ;
VkImageCopy copyRegion { } ;
copyRegion . srcSubresource = { VK_IMAGE_ASPECT_COLOR_BIT , 0 , 0 , 1 } ;
copyRegion . srcOffset = { 0 , 0 , 0 } ;
copyRegion . dstSubresource = { VK_IMAGE_ASPECT_COLOR_BIT , 0 , 0 , 1 } ;
copyRegion . dstOffset = { 0 , 0 , 0 } ;
copyRegion . extent = { width , height , 1 } ;
vkCmdCopyImage ( drawCmdBuffers [ i ] , storageImage . image , VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL , swapChain . images [ i ] , VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL , 1 , & copyRegion ) ;
// Transition swap chain image back for presentation
vks : : tools : : setImageLayout (
drawCmdBuffers [ i ] ,
swapChain . images [ i ] ,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ,
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR ,
subresourceRange ) ;
// Transition ray tracing output image back to general layout
vks : : tools : : setImageLayout (
drawCmdBuffers [ i ] ,
storageImage . image ,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL ,
VK_IMAGE_LAYOUT_GENERAL ,
subresourceRange ) ;
drawUI ( drawCmdBuffers [ i ] , frameBuffers [ i ] ) ;
VK_CHECK_RESULT ( vkEndCommandBuffer ( drawCmdBuffers [ i ] ) ) ;
}
}
void updateUniformBuffers ( )
{
uniformData . projInverse = glm : : inverse ( camera . matrices . perspective ) ;
uniformData . viewInverse = glm : : inverse ( camera . matrices . view ) ;
2023-05-13 16:19:40 +02:00
uniformData . lightPos = glm : : vec4 ( cos ( glm : : radians ( timer * 360.0f ) ) * 60.0f , 0.0f , 25.0f + sin ( glm : : radians ( timer * 360.0f ) ) * 60.0f , 0.0f ) ;
2023-05-13 12:22:04 +02:00
memcpy ( ubo . mapped , & uniformData , sizeof ( uniformData ) ) ;
}
void getEnabledFeatures ( )
{
// Enable features required for ray tracing using feature chaining via pNext
enabledBufferDeviceAddresFeatures . sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES ;
enabledBufferDeviceAddresFeatures . bufferDeviceAddress = VK_TRUE ;
enabledRayTracingPipelineFeatures . sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR ;
enabledRayTracingPipelineFeatures . rayTracingPipeline = VK_TRUE ;
enabledRayTracingPipelineFeatures . pNext = & enabledBufferDeviceAddresFeatures ;
enabledAccelerationStructureFeatures . sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR ;
enabledAccelerationStructureFeatures . accelerationStructure = VK_TRUE ;
enabledAccelerationStructureFeatures . pNext = & enabledRayTracingPipelineFeatures ;
deviceCreatepNextChain = & enabledAccelerationStructureFeatures ;
}
void prepare ( )
{
VulkanRaytracingSample : : prepare ( ) ;
createBuffers ( ) ;
// Create the acceleration structures used to render the ray traced scene
createBottomLevelAccelerationStructure ( ) ;
createTopLevelAccelerationStructure ( ) ;
createStorageImage ( swapChain . colorFormat , { width , height , 1 } ) ;
createUniformBuffer ( ) ;
createRayTracingPipeline ( ) ;
createShaderBindingTables ( ) ;
createDescriptorSets ( ) ;
buildCommandBuffers ( ) ;
prepared = true ;
}
void draw ( )
{
VulkanExampleBase : : prepareFrame ( ) ;
submitInfo . commandBufferCount = 1 ;
submitInfo . pCommandBuffers = & drawCmdBuffers [ currentBuffer ] ;
VK_CHECK_RESULT ( vkQueueSubmit ( queue , 1 , & submitInfo , VK_NULL_HANDLE ) ) ;
VulkanExampleBase : : submitFrame ( ) ;
}
virtual void render ( )
{
if ( ! prepared )
return ;
draw ( ) ;
if ( ! paused | | camera . updated )
updateUniformBuffers ( ) ;
}
} ;
VULKAN_EXAMPLE_MAIN ( )