Bootstrap

Vulkan 教程第七部分:光线追踪

目录

1. 初始化光线追踪扩展

启用光线追踪扩展

检查扩展支持

启用光线追踪功能

2. 创建加速结构

底层加速结构

顶层加速结构

3. 创建光线追踪管线

光线追踪着色器

创建光线追踪管线

4. 执行光线追踪

记录光线追踪命令缓冲

4. 执行光线追踪

提交光线追踪命令缓冲


1. 初始化光线追踪扩展

首先,我们需要确保启用了必要的扩展和功能。

启用光线追踪扩展
std::vector<const char*> deviceExtensions = {
    VK_KHR_SWAPCHAIN_EXTENSION_NAME,
    VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME,
    VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME,
    VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME,
    VK_KHR_SPIRV_1_4_EXTENSION_NAME,
    VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME
};
检查扩展支持
bool checkDeviceExtensionSupport(VkPhysicalDevice device) {
    uint32_t extensionCount;
    vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr);

    std::vector<VkExtensionProperties> availableExtensions(extensionCount);
    vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data());

    std::set<std::string> requiredExtensions(deviceExtensions.begin(), deviceExtensions.end());

    for (const auto& extension : availableExtensions) {
        requiredExtensions.erase(extension.extensionName);
    }

    return requiredExtensions.empty();
}

启用光线追踪功能
void enableRayTracingFeatures() {
    VkPhysicalDeviceRayTracingPipelineFeaturesKHR rayTracingPipelineFeatures{};
    rayTracingPipelineFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR;
    rayTracingPipelineFeatures.rayTracingPipeline = VK_TRUE;

    VkPhysicalDeviceAccelerationStructureFeaturesKHR accelerationStructureFeatures{};
    accelerationStructureFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR;
    accelerationStructureFeatures.accelerationStructure = VK_TRUE;

    VkPhysicalDeviceFeatures2 deviceFeatures2{};
    deviceFeatures2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
    deviceFeatures2.pNext = &rayTracingPipelineFeatures;
    rayTracingPipelineFeatures.pNext = &accelerationStructureFeatures;

    vkGetPhysicalDeviceFeatures2(physicalDevice, &deviceFeatures2);
}
2. 创建加速结构

加速结构用于加速光线追踪的碰撞检测。我们需要创建底层加速结构(BLAS)和顶层加速结构(TLAS)。

底层加速结构
VkAccelerationStructureKHR bottomLevelAS;
VkDeviceMemory bottomLevelASMemory;

void createBottomLevelAS() {
    // Define geometry
    VkAccelerationStructureGeometryKHR geometry{};
    geometry.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR;
    geometry.geometryType = VK_GEOMETRY_TYPE_TRIANGLES_KHR;
    geometry.flags = VK_GEOMETRY_OPAQUE_BIT_KHR;
    geometry.geometry.triangles.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_TRIANGLES_DATA_KHR;
    geometry.geometry.triangles.vertexFormat = VK_FORMAT_R32G32B32_SFLOAT;
    geometry.geometry.triangles.vertexData.deviceAddress = getBufferDeviceAddress(vertexBuffer);
    geometry.geometry.triangles.maxVertex = static_cast<uint32_t>(vertices.size());
    geometry.geometry.triangles.vertexStride = sizeof(Vertex);
    geometry.geometry.triangles.indexType = VK_INDEX_TYPE_UINT16;
    geometry.geometry.triangles.indexData.deviceAddress = getBufferDeviceAddress(indexBuffer);

    // Build information
    VkAccelerationStructureBuildGeometryInfoKHR buildInfo{};
    buildInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR;
    buildInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR;
    buildInfo.flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR;
    buildInfo.geometryCount = 1;
    buildInfo.pGeometries = &geometry;

    // Size information
    VkAccelerationStructureBuildSizesInfoKHR sizeInfo{};
    sizeInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR;
    vkGetAccelerationStructureBuildSizesKHR(device, VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, &buildInfo, maxPrimitiveCounts.data(), &sizeInfo);

    // Create bottom level acceleration structure
    VkAccelerationStructureCreateInfoKHR createInfo{};
    createInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR;
    createInfo.buffer = bottomLevelASBuffer;
    createInfo.size = sizeInfo.accelerationStructureSize;
    createInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR;
    if (vkCreateAccelerationStructureKHR(device, &createInfo, nullptr, &bottomLevelAS) != VK_SUCCESS) {
        throw std::runtime_error("failed to create bottom level acceleration structure!");
    }

    // Build command buffer
    VkCommandBuffer commandBuffer = beginSingleTimeCommands();

    VkAccelerationStructureBuildGeometryInfoKHR buildInfo{};
    buildInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR;
    buildInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR;
    buildInfo.flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR;
    buildInfo.geometryCount = 1;
    buildInfo.pGeometries = &geometry;
    buildInfo.scratchData.deviceAddress = scratchBufferDeviceAddress;
    buildInfo.dstAccelerationStructure = bottomLevelAS;

    VkAccelerationStructureBuildRangeInfoKHR buildRangeInfo{};
    buildRangeInfo.primitiveCount = static_cast<uint32_t>(indices.size()) / 3;
    buildRangeInfo.primitiveOffset = 0;
    buildRangeInfo.firstVertex = 0;
    buildRangeInfo.transformOffset = 0;

    std::vector<const VkAccelerationStructureBuildRangeInfoKHR*> buildRangeInfos = { &buildRangeInfo };
    vkCmdBuildAccelerationStructuresKHR(commandBuffer, 1, &buildInfo, buildRangeInfos.data());

    endSingleTimeCommands(commandBuffer);

    // Free scratch buffer
    vkDestroyBuffer(device, scratchBuffer, nullptr);
    vkFreeMemory(device, scratchBufferMemory, nullptr);
}
  • VkAccelerationStructureGeometryKHR 结构体定义加速结构的几何信息。
  • VkAccelerationStructureBuildGeometryInfoKHR 结构体包含加速结构的构建信息。
  • VkAccelerationStructureBuildSizesInfoKHR 结构体包含加速结构的大小信息。
  • vkCreateAccelerationStructureKHR 函数创建加速结构。
  • vkCmdBuildAccelerationStructuresKHR 函数在命令缓冲中记录加速结构的构建命令。
顶层加速结构
VkAccelerationStructureKHR topLevelAS;
VkDeviceMemory topLevelASMemory;

void createTopLevelAS() {
    // Define instance
    VkAccelerationStructureInstanceKHR instance{};
    instance.transform = transformMatrix;  // 4x3 transform matrix
    instance.instanceCustomIndex = 0;
    instance.mask = 0xFF;
    instance.instanceShaderBindingTableRecordOffset = 0;
    instance.flags = VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR;
    instance.accelerationStructureReference = bottomLevelASAddress;

    VkBuffer instanceBuffer;
    VkDeviceMemory instanceBufferMemory;
    createBuffer(sizeof(instance), 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, instanceBuffer, instanceBufferMemory);

    void* data;
    vkMapMemory(device, instanceBufferMemory, 0, sizeof(instance), 0, &data);
    memcpy(data, &instance, sizeof(instance));
    vkUnmapMemory(device, instanceBufferMemory);

    VkAccelerationStructureGeometryKHR geometry{};
    geometry.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR;
    geometry.geometryType = VK_GEOMETRY_TYPE_INSTANCES_KHR;
    geometry.geometry.instances.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_INSTANCES_DATA_KHR;
    geometry.geometry.instances.data.deviceAddress = getBufferDeviceAddress(instanceBuffer);
    geometry.flags = VK_GEOMETRY_OPAQUE_BIT_KHR;

    VkAccelerationStructureBuildGeometryInfoKHR buildInfo{};
    buildInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR;
    buildInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR;
    buildInfo.flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR;
    buildInfo.geometryCount = 1;
    buildInfo.pGeometries = &geometry;
    buildInfo.srcAccelerationStructure = VK_NULL_HANDLE;
    buildInfo.dstAccelerationStructure = topLevelAS;
    buildInfo.scratchData.deviceAddress = scratchBufferDeviceAddress;

    VkAccelerationStructureBuildSizesInfoKHR sizeInfo{};
    sizeInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR;
    vkGetAccelerationStructureBuildSizesKHR(device, VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, &buildInfo, maxPrimitiveCounts.data(), &sizeInfo);

    VkAccelerationStructureCreateInfoKHR createInfo{};
    createInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR;
    createInfo.buffer = topLevelASBuffer;
    createInfo.size = sizeInfo.accelerationStructureSize;
    createInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR;
    if (vkCreateAccelerationStructureKHR(device, &createInfo, nullptr, &topLevelAS) != VK_SUCCESS) {
        throw std::runtime_error("failed to create top level acceleration structure!");
    }

    VkCommandBuffer commandBuffer = beginSingleTimeCommands();

    vkCmdBuildAccelerationStructuresKHR(commandBuffer, 1, &buildInfo, &buildRangeInfo);

    endSingleTimeCommands(commandBuffer);

    // Free scratch buffer
    vkDestroyBuffer(device, scratchBuffer, nullptr);
    vkFreeMemory(device, scratchBufferMemory, nullptr);
}

  • VkAccelerationStructureInstanceKHR 结构体定义加速结构实例的信息。
  • createBuffer 函数创建并分配缓冲,用于存储实例数据。
  • VkAccelerationStructureGeometryKHR 结构体定义顶层加速结构的几何信息。
  • VkAccelerationStructureBuildGeometryInfoKHR 结构体包含顶层加速结构的构建信息。
  • vkCreateAccelerationStructureKHR 函数创建顶层加速结构。
  • vkCmdBuildAccelerationStructuresKHR 函数在命令缓冲中记录加速结构的构建命令。
3. 创建光线追踪管线
光线追踪着色器
#version 460
#extension GL_EXT_ray_tracing : require

layout(set = 0, binding = 0) uniform accelerationStructureEXT topLevelAS;
layout(set = 0, binding = 1, rgba32f) uniform image2D resultImage;

layout(location = 0) rayPayloadEXT vec3 payload;

void main() {
    vec3 origin = vec3(0.0, 0.0, -5.0);
    vec3 direction = vec3(0.0, 0.0, 1.0);
    uint rayFlags = gl_RayFlagsOpaqueEXT;

    traceRayEXT(topLevelAS, rayFlags, 0xFF, 0, 1, 0, origin, 0.0, direction, 10000.0, 0);
    imageStore(resultImage, ivec2(gl_LaunchIDEXT.xy), vec4(payload, 1.0));
}

编译光线追踪着色器:

glslc raygen_shader.glsl -o raygen_shader.spv
创建光线追踪管线
VkPipeline rayTracingPipeline;
VkPipelineLayout rayTracingPipelineLayout;

void createRayTracingPipeline() {
    auto raygenShaderCode = readFile("raygen_shader.spv");
    VkShaderModule raygenShaderModule = createShaderModule(raygenShaderCode);

    VkPipelineShaderStageCreateInfo raygenShaderStageInfo{};
    raygenShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
    raygenShaderStageInfo.stage = VK_SHADER_STAGE_RAYGEN_BIT_KHR;
    raygenShaderStageInfo.module = raygenShaderModule;
    raygenShaderStageInfo.pName = "main";

    VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
    pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
    pipelineLayoutInfo.setLayoutCount = 1;
    pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout;

    if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &rayTracingPipelineLayout) != VK_SUCCESS) {
        throw std::runtime_error("failed to create ray tracing pipeline layout!");
    }

    VkRayTracingPipelineCreateInfoKHR pipelineInfo{};
    pipelineInfo.sType = VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_KHR;
    pipelineInfo.stageCount = 1;
    pipelineInfo.pStages = &raygenShaderStageInfo;
    pipelineInfo.groupCount = 1;
    pipelineInfo.pGroups = &raygenShaderGroup;
    pipelineInfo.maxPipelineRayRecursionDepth = 1;
    pipelineInfo.layout = rayTracingPipelineLayout;

    if (vkCreateRayTracingPipelinesKHR(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &rayTracingPipeline) != VK_SUCCESS) {
        throw std::runtime_error("failed to create ray tracing pipeline!");
    }

    vkDestroyShaderModule(device, raygenShaderModule, nullptr);
}
  • VkPipelineShaderStageCreateInfo 结构体定义光线追踪着色器阶段的信息。
  • VkPipelineLayoutCreateInfo 结构体包含创建管线布局的信息。
  • VkRayTracingPipelineCreateInfoKHR 结构体包含创建光线追踪管线的信息。
  • vkCreateRayTracingPipelinesKHR 函数创建光线追踪管线。
4. 执行光线追踪
记录光线追踪命令缓冲
VkCommandBuffer rayTracingCommandBuffer;

void recordRayTracingCommandBuffer() {
    VkCommandBufferBeginInfo beginInfo{};
    beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;

    if (vkBeginCommandBuffer(rayTracingCommandBuffer, &beginInfo) != VK_SUCCESS) {
        throw std::runtime_error("failed to begin recording ray tracing command buffer!");
    }

    vkCmdBindPipeline(rayTracingCommandBuffer, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, rayTracingPipeline);
    vkCmdBindDescriptorSets(rayTracingCommandBuffer, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, rayTracingPipelineLayout, 0, 1, &descriptorSet, 0, nullptr);

    VkStridedDeviceAddressRegionKHR raygenShaderSbtEntry{};
    raygenShaderSbtEntry.deviceAddress = getShaderBindingTableAddress(raygenSbtBuffer);
    raygenShaderSbtEntry.stride = shaderGroupHandleSize;
    raygenShaderSbtEntry.size = shaderGroupHandleSize;

    vkCmdTraceRaysKHR(rayTracingCommandBuffer, &raygenShaderSbtEntry, &missShaderSbtEntry, &hitShaderSbtEntry, &callableShaderSbtEntry, WIDTH, HEIGHT, 1);

    if (vkEndCommandBuffer(rayTracingCommandBuffer) != VK_SUCCESS) {
        throw std::runtime_error("failed to record ray tracing command buffer!");
    }
}

  • vkCmdBindPipeline 函数绑定光线追踪管线。
  • vkCmdBindDescriptorSets 函数绑定描述符集。
  • VkStridedDeviceAddressRegionKHR 结构体定义着色器绑定表的地址和大小信息。
  • vkCmdTraceRaysKHR 函数执行光线追踪。
提交光线追踪命令缓冲
void submitRayTracingCommandBuffer() {
    VkSubmitInfo submitInfo{};
    submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
    submitInfo.commandBufferCount = 1;
    submitInfo.pCommandBuffers = &rayTracingCommandBuffer;

    if (vkQueueSubmit(computeQueue, 1, &submitInfo, VK_NULL_HANDLE) != VK_SUCCESS) {
        throw std::runtime_error("failed to submit ray tracing command buffer!");
    }

    vkQueueWaitIdle(computeQueue);
}
  • VkSubmitInfo 结构体包含提交命令缓冲的信息。
  • vkQueueSubmit 函数提交命令缓冲到计算队列。
  • vkQueueWaitIdle 函数等待计算队列完成所有提交的命令缓冲。

 

;