RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 1186537
Accepted
Alex Nem
Alex Nem
Asked:2020-10-06 03:17:01 +0000 UTC2020-10-06 03:17:01 +0000 UTC 2020-10-06 03:17:01 +0000 UTC

Vulkan Ray Tracing (VK_KHR_ray_tracing) - 访问顶点属性

  • 772

我正在尝试在 Vulkan 上掌握光线追踪,主要信息来源是本教程。我想出了如何构建 BLAS 和 TLAS,创建了一个 SBT 表,最后开始跟踪场景: 在此处输入图像描述

我正要继续简单照明的实现,然后出现了一个问题,我在教程中找不到答案..

如何获取顶点属性?

最让我困惑的是:

  1. 在我最初为光栅器制作的渲染器中,场景由一组网格描述,其中每个网格都有自己的顶点和索引缓冲区。
  2. 在光线追踪的情况下,这些缓冲区用作 BLAS(低级加速结构)的基础,但 BLAS 仅包含位置,并且无法访问 BLAS 中的属性。
  3. 有一个 TLAS(顶级加速结构),它基本上设置了光线追踪的场景。有一个实例这样的概念,它已经引用了 BLAS(也就是说,场景可以由许多不同的缓冲区来描述,而不仅仅是一个,正如它已经暗示的那样)
  4. 有了这一切,关于单个实例的顶点属性什么都没有说,并且着色器在相交期间可以获得的最大值是重心坐标和一些gl_PrimitiveID(据我了解,存储三角形的索引),以及作为gl_InstanceID(据我了解,TLAS 中的 instance'a 索引)

所有这些都引出了以下问题:

  1. 这一切是否意味着我需要将几何图形重新加载到某种 SSBO 中才能访问顶点属性。
  2. 如果是这样,那么如果我的场景是由不同的顶点缓冲区描述的,而不是一个大的,在这种情况下我该怎么办?事实证明,您需要创建某种将存储在 SSBO 中的二维数组,其键将是实例 ID?
  3. 如果是这样,并且如果制作了这样的数组,是否意味着除了顶点本身之外还需要存储变换,以便获得属性的实际值(例如,法线)?

我上面提到的教程显示了以下着色器代码:

layout(binding = 2, set = 1, scalar) buffer ScnDesc { sceneDesc i[]; } scnDesc;
layout(binding = 5, set = 1, scalar) buffer Vertices { Vertex v[]; } vertices[];
layout(binding = 6, set = 1) buffer Indices { uint i[]; } indices[];

// Object of this instance
uint objId = scnDesc.i[gl_InstanceID].objId;

// Indices of the triangle
ivec3 ind = ivec3(indices[nonuniformEXT(objId)].i[3 * gl_PrimitiveID + 0],   //
                indices[nonuniformEXT(objId)].i[3 * gl_PrimitiveID + 1],   //
                indices[nonuniformEXT(objId)].i[3 * gl_PrimitiveID + 2]);  //
// Vertex of the triangle
Vertex v0 = vertices[nonuniformEXT(objId)].v[ind.x];
Vertex v1 = vertices[nonuniformEXT(objId)].v[ind.y];
Vertex v2 = vertices[nonuniformEXT(objId)].v[ind.z];

但我不太明白这里发生了什么。什么是 nonuniformEXT,为什么会这样?还有一个顶点数组和一个索引数组作为描述符......一个一维数组......好像它包含场景的所有几何形状。

总的来说,我对此完全感到困惑,如果有人能阐明这一切,我将不胜感激。

c++
  • 1 1 个回答
  • 10 Views

1 个回答

  • Voted
  1. Best Answer
    Alex Nem
    2020-10-08T00:35:10Z2020-10-08T00:35:10Z

    于是,一切都尘埃落定。

    确实没有办法从 BLAS 中提取顶点属性,但这并不意味着不能重用几何缓冲区(用于 BLAS)来提取这些相同的属性。

    在以下代码中突出显示

    layout(binding = 5, set = 1, scalar) buffer Vertices { Vertex v[]; } vertices[];
    layout(binding = 6, set = 1) buffer Indices { uint i[]; } indices[];
    

    是这些不是一维数组。这些是描述符数组。也就是说,这样一个数组的每个元素对应一个缓冲区。

    您可以将多个缓冲区绑定为 SSBO 并访问它们。gl_InstanceID 可以用作这样的描述符数组的键。

    因此,例如,如果我们在舞台上显示 5 个对象,每个对象都有一个指向其自己的几何缓冲区(顶点和索引缓冲区)的链接 - 您可以简单地将 5 个描述符绑定为数组元素。

    gl_PrimitiveID 是射线相交的三角形的索引,可用于获取所需的索引。并在提取的索引的帮助下,访问所需的顶点。

    如何准备这个描述符数组?

    1. 确保设备扩展已连接VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME并且VK_EXT_SCALAR_BLOCK_LAYOUT_EXTENSION_NAME

    2. 我们确保启用了必要的物理功能。创建逻辑设备时的设备:

      vk::PhysicalDeviceVulkan12Features vulkan12Features{};
      vulkan12Features.setDescriptorBindingVariableDescriptorCount(VK_TRUE);
      vulkan12Features.setRuntimeDescriptorArray(VK_TRUE);
      vulkan12Features.setDescriptorIndexing(VK_TRUE);
      vulkan12Features.setShaderStorageBufferArrayNonUniformIndexing(VK_TRUE);
      
    3. 在创建描述符池时,我们指定描述符的类型 - eStorageBuffer 和数量,该数量应等于场景中单个对象(网格)的最大数量。

       // Создать пул для набора используемого в трассировке лучей
       {
           // Размеры пула для наборов типа "набор для трассировки лучей"
           std::vector<vk::DescriptorPoolSize> descriptorPoolSizes = {
                   // Дескриптор структуры ускорения верхнего уровня
                   {vk::DescriptorType::eAccelerationStructureKHR, 1},
                   // Дескриптор структуры итогового изображения (результат трассировки)
                   {vk::DescriptorType::eStorageImage, 1},
      
                   // Дескрипторы storage-буферов хранящих индексы (массив дескрипторов),
                   {vk::DescriptorType::eStorageBuffer, static_cast<uint32_t>(maxMeshes)},
                   // Дескрипторы storage-буферов хранящих вершины (массив дескрипторов)
                   {vk::DescriptorType::eStorageBuffer, static_cast<uint32_t>(maxMeshes)},
                   // Дескрипторы uniform-бферов хранящих матрицы трансфомацияя (массив дескрипторов)
                   {vk::DescriptorType::eStorageBuffer, static_cast<uint32_t>(maxMeshes)}
      
           };
      
           // Нам нужен один набор данного типа, он будет привязываться единожды за кадр
           vk::DescriptorPoolCreateInfo descriptorPoolCreateInfo{};
           descriptorPoolCreateInfo.poolSizeCount = descriptorPoolSizes.size();
           descriptorPoolCreateInfo.pPoolSizes = descriptorPoolSizes.data();
           descriptorPoolCreateInfo.maxSets = 1;
           descriptorPoolCreateInfo.flags = vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet;
           descriptorPoolRayTracing_ = device_.getLogicalDevice()->createDescriptorPoolUnique(descriptorPoolCreateInfo);
       }
      
    4. 不要忘记描述符集布局

       // Макет размещения набора используемого в трассировке лучей
       {
           // Описание привязок
           std::vector<vk::DescriptorSetLayoutBinding> bindings = {
                   // Структура ускорения верхнего уровня
                   {
                           0,
                           vk::DescriptorType::eAccelerationStructureKHR,
                           1,
                           vk::ShaderStageFlagBits::eRaygenKHR|vk::ShaderStageFlagBits::eClosestHitKHR|vk::ShaderStageFlagBits::eAnyHitKHR,
                           nullptr,
                   },
                   // Хранимое изображение, результат трассировки
                   {
                           1,
                           vk::DescriptorType::eStorageImage,
                           1,
                           vk::ShaderStageFlagBits::eRaygenKHR,
                           nullptr,
                   },
                   // Харнимый буфер индексов (массив дескрипторов)
                   {
                       2,
                       vk::DescriptorType::eStorageBuffer,
                       static_cast<uint32_t>(maxMeshes),
                       vk::ShaderStageFlagBits::eRaygenKHR|vk::ShaderStageFlagBits::eClosestHitKHR|vk::ShaderStageFlagBits::eAnyHitKHR,
                       nullptr
                   },
                   // Харнимый буфер вершин (массив дескрипторов)
                   {
                       3,
                       vk::DescriptorType::eStorageBuffer,
                       static_cast<uint32_t>(maxMeshes),
                       vk::ShaderStageFlagBits::eRaygenKHR|vk::ShaderStageFlagBits::eClosestHitKHR|vk::ShaderStageFlagBits::eAnyHitKHR,
                       nullptr
                   },
                   // Хранимый буфер матриц модели (массив дескрипторов)
                   {
                       4,
                       vk::DescriptorType::eStorageBuffer,
                       static_cast<uint32_t>(maxMeshes),
                       vk::ShaderStageFlagBits::eRaygenKHR|vk::ShaderStageFlagBits::eClosestHitKHR|vk::ShaderStageFlagBits::eAnyHitKHR,
                       nullptr
                   }
           };
      
           // Создать макет размещения дескрипторного набора
           vk::DescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo{};
           descriptorSetLayoutCreateInfo.bindingCount = bindings.size();
           descriptorSetLayoutCreateInfo.pBindings = bindings.data();
           descriptorSetLayoutRayTracing_ = device_.getLogicalDevice()->createDescriptorSetLayoutUnique(descriptorSetLayoutCreateInfo);
      
    5. 在循环中更新(绑定)描述符和特定缓冲区(我们遍历场景的所有当前元素并将它们的索引、顶点和 UBO 缓冲区与必要的描述符连接)

      // Г е о м е т р и ч е с к и е  +  U B O  б у ф е р ы  м е ш е й
      
       std::vector<vk::DescriptorBufferInfo> indexBufferInfos;
       std::vector<vk::DescriptorBufferInfo> vertexBufferInfos;
       std::vector<vk::DescriptorBufferInfo> uboBufferInfos;
      
       for(auto & sceneMesh : sceneMeshes_)
       {
           indexBufferInfos.emplace_back(
                   sceneMesh->getGeometryBuffer()->getIndexBuffer().getBuffer().get(),
                   0,
                   VK_WHOLE_SIZE);
      
           vertexBufferInfos.emplace_back(
                   sceneMesh->getGeometryBuffer()->getVertexBuffer().getBuffer().get(),
                   0,
                   VK_WHOLE_SIZE);
      
           uboBufferInfos.emplace_back(
                   sceneMesh->getModelMatrixUbo().getBuffer().get(),
                   0,
                   VK_WHOLE_SIZE);
       }
      
       vk::WriteDescriptorSet writeIndexBuffers{};
       writeIndexBuffers.setDstSet(rtDescriptorSet_.get());
       writeIndexBuffers.setDstBinding(2);
       writeIndexBuffers.setDstArrayElement(0);
       writeIndexBuffers.setDescriptorCount(static_cast<uint32_t>(indexBufferInfos.size()));
       writeIndexBuffers.setDescriptorType(vk::DescriptorType::eStorageBuffer);
       writeIndexBuffers.setPBufferInfo(indexBufferInfos.data());
       writes.push_back(writeIndexBuffers);
      
       vk::WriteDescriptorSet writeVertexBuffers{};
       writeVertexBuffers.setDstSet(rtDescriptorSet_.get());
       writeVertexBuffers.setDstBinding(3);
       writeVertexBuffers.setDstArrayElement(0);
       writeVertexBuffers.setDescriptorCount(static_cast<uint32_t>(vertexBufferInfos.size()));
       writeVertexBuffers.setDescriptorType(vk::DescriptorType::eStorageBuffer);
       writeVertexBuffers.setPBufferInfo(vertexBufferInfos.data());
       writes.push_back(writeVertexBuffers);
      
       vk::WriteDescriptorSet writeUboBuffers{};
       writeUboBuffers.setDstSet(rtDescriptorSet_.get());
       writeUboBuffers.setDstBinding(4);
       writeUboBuffers.setDstArrayElement(0);
       writeUboBuffers.setDescriptorCount(static_cast<uint32_t>(uboBufferInfos.size()));
       writeUboBuffers.setDescriptorType(vk::DescriptorType::eStorageBuffer);
       writeUboBuffers.setPBufferInfo(uboBufferInfos.data());
       writes.push_back(writeUboBuffers);
      
      
       // Связываем дескрипторы с ресурсами
       device_.getLogicalDevice()->updateDescriptorSets(writes.size(), writes.data(), 0, nullptr);
      
    6. 我们确保所有用作此类 SSBO 的缓冲区都有一个使用标志vk::BufferUsageFlagBits::eStorageBuffer

    因此,着色器将可以访问所有必要的数据,并且可以使用重心坐标插入必要的顶点属性。

    // ID текущего объекта
    uint objId = gl_InstanceID;
    
    // Матрица модели
    mat4 modelMatrix = _matrices[objId].m;
    
    // Матрица преобразования нормалей
    mat3 normalMatrix = transpose(inverse(mat3(modelMatrix)));
    
    // Индексы вершин в индексном буфере для треугольника, с которым произошло пересечение (gl_PrimitiveID - индекс треугольника)
    ivec3 ind = ivec3(_indices[objId].i[3 * gl_PrimitiveID + 0],
                      _indices[objId].i[3 * gl_PrimitiveID + 1],
                      _indices[objId].i[3 * gl_PrimitiveID + 2]);
    
    // Вершины треугольников в буфере вершин (достаем при помощи полученных индексов)
    Vertex[3] vertices = Vertex[3](_vertices[objId].v[ind.x],
                                   _vertices[objId].v[ind.y],
                                   _vertices[objId].v[ind.z]);
    
    // Интерполированное значение в точке пересечения
    Vertex interpolated = interpolatedVertex(vertices, attribs);
    
    // Перевести в глобальное пространство из пространства модели
    interpolated.normal = normalize(normalMatrix * interpolated.normal);
    interpolated.position = (modelMatrix * vec4(interpolated.position,1.0f)).xyz;
    

    也有这样一个时刻,验证层可以发誓调用没有更新的描述符。这一切都是因为在创建描述符集布局时,指定了描述符的最大数量,而不是实际使用的描述符。如果您指定将在那里使用的数量,那么一切都会好起来的。其实这个层警告可以忽略(或者创建一个描述符集布局,描述符个数对应场景元素个数)

    也许这对某人有用。

    • 2

相关问题

  • 编译器和模板处理

  • 指针。找到最小数量

  • C++,关于枚举类对象初始化的问题

  • 函数中的二维数组

  • 无法使用默认构造函数创建类对象

  • C++ 和循环依赖

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    如何从列表中打印最大元素(str 类型)的长度?

    • 2 个回答
  • Marko Smith

    如何在 PyQT5 中清除 QFrame 的内容

    • 1 个回答
  • Marko Smith

    如何将具有特定字符的字符串拆分为两个不同的列表?

    • 2 个回答
  • Marko Smith

    导航栏活动元素

    • 1 个回答
  • Marko Smith

    是否可以将文本放入数组中?[关闭]

    • 1 个回答
  • Marko Smith

    如何一次用多个分隔符拆分字符串?

    • 1 个回答
  • Marko Smith

    如何通过 ClassPath 创建 InputStream?

    • 2 个回答
  • Marko Smith

    在一个查询中连接多个表

    • 1 个回答
  • Marko Smith

    对列表列表中的所有值求和

    • 3 个回答
  • Marko Smith

    如何对齐 string.Format 中的列?

    • 1 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5