文章目录
前言
使用新的光线追踪扩展将光线追踪阴影投射添加到更复杂的场景中。展示如何添加多个命中和未命中着色器以及如何修改现有着色器以添加阴影计算。
一、base中的头文件(VulkanRaytracingSample.h)
1.类的私有函数与公共变量
class VulkanRaytracingSample : public VulkanExampleBase
{
protected:
// 使用不同颜色的附件加载操作更新默认渲染通道
virtual void updateRenderPass();
public:
// Function pointers for ray tracing related stuff
// 光线追踪相关内容的函数指针
PFN_vkGetBufferDeviceAddressKHR vkGetBufferDeviceAddressKHR;
PFN_vkCreateAccelerationStructureKHR vkCreateAccelerationStructureKHR;
PFN_vkDestroyAccelerationStructureKHR vkDestroyAccelerationStructureKHR;
PFN_vkGetAccelerationStructureBuildSizesKHR vkGetAccelerationStructureBuildSizesKHR;
PFN_vkGetAccelerationStructureDeviceAddressKHR vkGetAccelerationStructureDeviceAddressKHR;
PFN_vkBuildAccelerationStructuresKHR vkBuildAccelerationStructuresKHR;
PFN_vkCmdBuildAccelerationStructuresKHR vkCmdBuildAccelerationStructuresKHR;
PFN_vkCmdTraceRaysKHR vkCmdTraceRaysKHR;
PFN_vkGetRayTracingShaderGroupHandlesKHR vkGetRayTracingShaderGroupHandlesKHR;
PFN_vkCreateRayTracingPipelinesKHR vkCreateRayTracingPipelinesKHR;
// Available features and properties
// 可用的功能和属性
VkPhysicalDeviceRayTracingPipelinePropertiesKHR rayTracingPipelineProperties{};
VkPhysicalDeviceAccelerationStructureFeaturesKHR accelerationStructureFeatures{};
// Enabled features and properties
// 启用的功能和属性
VkPhysicalDeviceBufferDeviceAddressFeatures enabledBufferDeviceAddresFeatures{};
VkPhysicalDeviceRayTracingPipelineFeaturesKHR enabledRayTracingPipelineFeatures{};
VkPhysicalDeviceAccelerationStructureFeaturesKHR enabledAccelerationStructureFeatures{};
// Holds information for a ray tracing scratch buffer that is used as a temporary storage
struct ScratchBuffer
{
uint64_t deviceAddress = 0;
VkBuffer handle = VK_NULL_HANDLE;
VkDeviceMemory memory = VK_NULL_HANDLE;
};
// Holds information for a ray tracing acceleration structure
// 保存用作临时存储的光线追踪暂存缓冲区的信息
struct AccelerationStructure {
VkAccelerationStructureKHR handle;
uint64_t deviceAddress = 0;
VkDeviceMemory memory;
VkBuffer buffer;
};
// Holds information for a storage image that the ray tracing shaders output to
// 保存光线追踪着色器输出到的存储图像的信息
struct StorageImage {
VkDeviceMemory memory = VK_NULL_HANDLE;
VkImage image = VK_NULL_HANDLE;
VkImageView view = VK_NULL_HANDLE;
VkFormat format;
} storageImage;
// Extends the buffer class and holds information for a shader binding table
// 扩展缓冲区类并保存着色器绑定表的信息
class ShaderBindingTable : public vks::Buffer {
public:
VkStridedDeviceAddressRegionKHR stridedDeviceAddressRegion{};
};
// Set to true, to denote that the sample only uses ray queries (changes extension and render pass handling)
// 设置为 true,表示示例仅使用光线查询(更改扩展名和渲染传递处理)
bool rayQueryOnly = false;
2.公共函数
void enableExtensions();
ScratchBuffer createScratchBuffer(VkDeviceSize size);
void deleteScratchBuffer(ScratchBuffer& scratchBuffer);
void createAccelerationStructure(AccelerationStructure& accelerationStructure, VkAccelerationStructureTypeKHR type, VkAccelerationStructureBuildSizesInfoKHR buildSizeInfo);
void deleteAccelerationStructure(AccelerationStructure& accelerationStructure);
uint64_t getBufferDeviceAddress(VkBuffer buffer);
void createStorageImage(VkFormat format, VkExtent3D extent);
void deleteStorageImage();
VkStridedDeviceAddressRegionKHR getSbtEntryStridedDeviceAddressRegion(VkBuffer buffer, uint32_t handleCount);
void createShaderBindingTable(ShaderBindingTable& shaderBindingTable, uint32_t handleCount);
// Draw the ImGUI UI overlay using a render pass
void drawUI(VkCommandBuffer commandBuffer, VkFramebuffer framebuffer);
virtual void prepare();
};
二、raytracingshadows.cpp文件
1.引入库
代码如下(示例):
#include "VulkanRaytracingSample.h"
#include "VulkanglTFModel.h"
2.类以及类变量属性
代码如下(示例):
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;
int32_t vertexSize;
} uniformData;
vks::Buffer ubo;
VkPipeline pipeline;
VkPipelineLayout pipelineLayout;
VkDescriptorSet descriptorSet;
VkDescriptorSetLayout descriptorSetLayout;
vkglTF::Model scene;
2.主要函数-1建立加速结构
1)创建包含场景实际几何图形(顶点、三角形)的底层加速度结构。在本例中,我们将加载一个更复杂的场景,而不是一个简单的三角形。
/*
Create the bottom level acceleration structure contains the scene's actual geometry (vertices, triangles)
*/
void createBottomLevelAccelerationStructure()
{
// The shaders are accessing the vertex and index buffers of the scene, so the proper usage flag has to be set on the vertex and index buffers for the scene
// 着色器正在访问场景的顶点和索引缓冲区,因此必须在场景的顶点和索引缓冲区上设置正确的使用标志
vkglTF::memoryPropertyFlags = VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
const uint32_t glTFLoadingFlags = vkglTF::FileLoadingFlags::PreTransformVertices | vkglTF::FileLoadingFlags::PreMultiplyVertexColors | vkglTF::FileLoadingFlags::FlipY;
scene.loadFromFile(getAssetPath() + "models/vulkanscene_shadow.gltf", vulkanDevice, queue, glTFLoadingFlags);
VkDeviceOrHostAddressConstKHR vertexBufferDeviceAddress{};
VkDeviceOrHostAddressConstKHR indexBufferDeviceAddress{};
vertexBufferDeviceAddress.deviceAddress = getBufferDeviceAddress(scene.vertices.buffer);
indexBufferDeviceAddress.deviceAddress = getBufferDeviceAddress(scene.indices.buffer);
uint32_t numTriangles = static_cast<uint32_t>(scene.indices.count) / 3;
uint32_t maxVertex = scene.vertices.count;
// Build
VkAccelerationStructureGeometryKHR accelerationStructureGeometry = vks::initializers::accelerationStructureGeometryKHR();
accelerationStructureGeometry.flags = VK_GEOMETRY_OPAQUE_BIT_KHR;
accelerationStructureGeometry.geometryType = VK_GEOMETRY_TYPE_TRIANGLES_KHR;
accelerationStructureGeometry.geometry.triangles.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_TRIANGLES_DATA_KHR;
accelerationStructureGeometry.geometry.triangles.vertexFormat = VK_FORMAT_R32G32B32_SFLOAT;
accelerationStructureGeometry.geometry.triangles.vertexData = vertexBufferDeviceAddress;
accelerationStructureGeometry.geometry.triangles.maxVertex = maxVertex;
accelerationStructureGeometry.geometry.triangles.vertexStride = sizeof(vkglTF::Vertex);
accelerationStructureGeometry.geometry.triangles.indexType = VK_INDEX_TYPE_UINT32;
accelerationStructureGeometry.geometry.triangles.indexData = indexBufferDeviceAddress;
accelerationStructureGeometry.geometry.triangles.transformData.deviceAddress = 0;
accelerationStructureGeometry.geometry.triangles.transformData.hostAddress = nullptr;
// 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,
&numTriangles,
&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 = numTriangles;
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
// 一些实现可能支持在主机上构建加速结构(VkPhysicalDeviceAccelerationStructureFeaturesKHR->accelerationStructureHostCommands),但我们更喜欢设备构建
VkCommandBuffer commandBuffer = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
vkCmdBuildAccelerationStructuresKHR(
commandBuffer,
1,
&accelerationBuildGeometryInfo,
accelerationBuildStructureRangeInfos.data());
vulkanDevice->flushCommandBuffer(commandBuffer, queue);
deleteScratchBuffer(scratchBuffer);
}
2)顶级加速结构包含场景的对象实例
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);
// @todo: as return value?
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();
}
3.主要函数-2创建着色器绑定表
详细内容参考-Vulkan_Ray Tracing 06_着色器绑定表
创建绑定程序和顶级加速结构的着色器绑定表,此示例中使用的 SBT 布局
SBT: 包含了在光线追踪场景中可能被调用的 所有着色器集合 ,以及那些可能被传给shader的嵌入式参数。其实SBT就是一个array, array里面每个槽是一个ShaderRecord。每个ShaderRecord里装了这些东西: shader句柄, 若干函数句柄,函数输入参数。
ShaderRecord有以下几种类型:
- RayGenerationRecord :其中的shader句柄指向一个RayGenerationShader;
- HitGroupRecord:里面装着3个函数句柄,分别对应3个shader: Intersection Shader / Any-Hit Shader / Closet-Hit Shader;
- MissRecord:里面装着shader句柄指向一个MissShader。
/*
Create the Shader Binding Tables that binds the programs and top-level acceleration structure
SBT Layout used in this sample:
/-----------\
| raygen |
|-----------|
| miss |
|-----------|
| hit |
\-----------/
*/
1)创建shader绑定表
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);
// We are using two miss shaders
createShaderBindingTable(shaderBindingTables.miss, 2);
createShaderBindingTable(shaderBindingTables.hit, 1);
// Copy handles
memcpy(shaderBindingTables.raygen.mapped, shaderHandleStorage.data(), handleSize);
// We are using two miss shaders, so we need to get two handles for the miss shader binding table
//需要两个misstable
memcpy(shaderBindingTables.miss.mapped, shaderHandleStorage.data() + handleSizeAligned, handleSize * 2);
memcpy(shaderBindingTables.hit.mapped, shaderHandleStorage.data() + handleSizeAligned * 3, handleSize);
}
总结
本次总结如下:
- 头文件的内容以及调用函数
- 引入的库函数
- 建立两层加速结构
- 创建着色器绑定表