Skip to content

Commit

Permalink
Update source\Literature\NVIDIAVulkanRayTracingTutorial\extensions\gl…
Browse files Browse the repository at this point in the history
…TFScene.rst
  • Loading branch information
FuXiii committed Oct 26, 2023
1 parent 3dbc814 commit 2a262fa
Show file tree
Hide file tree
Showing 2 changed files with 203 additions and 0 deletions.
4 changes: 4 additions & 0 deletions source/Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
此更新日志为纵览更新,对于具体文章的更新位于每个文章的开头的 `更新记录` 中。
```

## 2023/10/26

>* 更新`glTF 场景`文档
## 2023/10/24

>* 增加`glTF 场景`文档
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ glTF 场景
* 2023/10/24 增加该扩展文档
* 2023/10/24 增加 ``教程`` 章节
* 2023/10/24 增加 ``场景数据`` 章节
* 2023/10/26 增加 ``加载 glTF 场景`` 章节
* 2023/10/26 增加 ``加载场景`` 章节
* 2023/10/26 增加 ``几何体转底层加速结构`` 章节
* 2023/10/26 增加 ``创建顶层加速结构`` 章节

`文献源`_

Expand Down Expand Up @@ -100,3 +104,198 @@ glTF 场景
nvvk::Buffer m_materialBuffer;
nvvk::Buffer m_primInfo;
nvvk::Buffer m_sceneDesc;

加载 glTF 场景
####################

我们这里将会使用 `TinyGLTF <https://github.com/syoyo/tinygltf>`_ 加载 ``glTF`` ,之后为了屏蔽掉繁琐的场景解析遍历,将会使用 `gltfScene <https://github.com/nvpro-samples/nvpro_core/tree/master/nvh#gltfscenehpp>`_ 来帮助我们将场景进行压缩。

加载场景
************************

相较于加载一个模型,这次我们将加载一个场景,所以我们使用 ``loadScene()`` 来代替 ``loadModel()`` 加载场景。

``loadScene()`` 函数在源文件中的开头将会引入 ``TinyGLTF`` 。

.. code:: c++

tinygltf::Model tmodel;
tinygltf::TinyGLTF tcontext;
std::string warn, error;
if(!tcontext.LoadASCIIFromFile(&tmodel, &error, &warn, filename))
assert(!"Error while loading scene");

之后我们将使用 ``gltfScene`` 来帮助我们压缩和提炼我们需要的数据。

.. code:: c++

m_gltfScene.importMaterials(tmodel);
m_gltfScene.importDrawableNodes(tmodel,
nvh::GltfAttributes::Normal | nvh::GltfAttributes::Texcoord_0);

接下来就是分配缓存并存储数据。比如,顶点位置,法线,纹理坐标等等。

.. code:: c++

// 在设备上创建换次年并拷贝顶点,索引和材质信息
nvvk::CommandPool cmdBufGet(m_device, m_graphicsQueueIndex);
VkCommandBuffer cmdBuf = cmdBufGet.createCommandBuffer();

m_vertexBuffer = m_alloc.createBuffer(cmdBuf, m_gltfScene.m_positions,
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT
| VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR);
m_indexBuffer = m_alloc.createBuffer(cmdBuf, m_gltfScene.m_indices,
VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT
| VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR);
m_normalBuffer = m_alloc.createBuffer(cmdBuf, m_gltfScene.m_normals,
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT
| VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT);
m_uvBuffer = m_alloc.createBuffer(cmdBuf, m_gltfScene.m_texcoords0,
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT
| VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT);
由于该示例的材质是一个简化版本,这里我们将从 ``glTF`` 材质中提取我们需要的部分。

.. code:: c++

// 仅获取我们需要的材质数据
std::vector<GltfShadeMaterial> shadeMaterials;
for(auto& m : m_gltfScene.m_materials)
{
shadeMaterials.emplace_back(GltfShadeMaterial{m.baseColorFactor, m.emissiveFactor, m.baseColorTexture});
}
m_materialBuffer = m_alloc.createBuffer(cmdBuf, shadeMaterials,
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT);

为了能够在最近命中着色器中找到命中位置,同时也能获取其它属性,我们将存储几何的偏移信息。

.. code:: c++

// 如下是用于在最近命中着色器中找到网格的图元信息
std::vector<PrimMeshInfo> primLookup;
for(auto& primMesh : m_gltfScene.m_primMeshes)
{
primLookup.push_back({primMesh.firstIndex, primMesh.vertexOffset, primMesh.materialIndex});
}
m_rtPrimLookup =
m_alloc.createBuffer(cmdBuf, primLookup, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT);

.. admonition:: std::vector<PrimMeshInfo> primLookup
:class: note

在本示例中一共有 ``10`` 个物体( ``5`` 面墙, ``3`` 个矩形, ``2`` 个球体)。然而解析完 ``glTF`` 场景后 ``primLookup`` 中有 ``9`` 个元素(模型)。其中顶面和底面墙体共用同一个网格。

最后创建一个缓存存储所以缓存的引用:

.. code:: c++

SceneDesc sceneDesc;
sceneDesc.vertexAddress = nvvk::getBufferDeviceAddress(m_device, m_vertexBuffer.buffer);
sceneDesc.indexAddress = nvvk::getBufferDeviceAddress(m_device, m_indexBuffer.buffer);
sceneDesc.normalAddress = nvvk::getBufferDeviceAddress(m_device, m_normalBuffer.buffer);
sceneDesc.uvAddress = nvvk::getBufferDeviceAddress(m_device, m_uvBuffer.buffer);
sceneDesc.materialAddress = nvvk::getBufferDeviceAddress(m_device, m_materialBuffer.buffer);
sceneDesc.primInfoAddress = nvvk::getBufferDeviceAddress(m_device, m_primInfo.buffer);
m_sceneDesc = m_alloc.createBuffer(cmdBuf, sizeof(SceneDesc), &sceneDesc,
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT);

在退出该函数之前,我们需要创建纹理(默认场景中没有纹理)并提交指令缓存。最后等到所有的数据拷贝完成。

.. code:: c++

// 创建所有找到的纹理
createTextureImages(cmdBuf, tmodel);
cmdBufGet.submitAndWait(cmdBuf);
m_alloc.finalizeAndReleaseStaging();


NAME_VK(m_vertexBuffer.buffer);
NAME_VK(m_indexBuffer.buffer);
NAME_VK(m_normalBuffer.buffer);
NAME_VK(m_uvBuffer.buffer);
NAME_VK(m_materialBuffer.buffer);
NAME_VK(m_primInfo.buffer);
NAME_VK(m_sceneDesc.buffer);
}

.. admonition:: NAME_VK
:class: note

宏 ``NAME_VK`` 是用于简化 ``Nsight Graphics`` 中 ``Vulkan`` 对象命名,用于调试时获取相应的创建信息。

几何体转底层加速结构
####################

我们不再使用 ``objectToVkGeometryKHR()`` 而会使用 ``primitiveToVkGeometry(const nvh::GltfPrimMesh& prim)`` 。该函数与之前的相似,仅仅是输入不同,这里 ``VkAccelerationStructureBuildRangeInfoKHR`` 将会设置偏移数据。

.. code:: c++

//--------------------------------------------------------------------------------------------------
// 将 glTF 中的网格转换成底层加速结构中的几何图元
//
auto HelloVulkan::primitiveToGeometry(const nvh::GltfPrimMesh& prim)
{
// 底层加速结构的构建需要原始的设备内存地址
VkDeviceAddress vertexAddress = nvvk::getBufferDeviceAddress(m_device, m_vertexBuffer.buffer);
VkDeviceAddress indexAddress = nvvk::getBufferDeviceAddress(m_device, m_indexBuffer.buffer);

uint32_t maxPrimitiveCount = prim.indexCount / 3;

// 设置顶点数组缓存
VkAccelerationStructureGeometryTrianglesDataKHR triangles{VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_TRIANGLES_DATA_KHR};
triangles.vertexFormat = VK_FORMAT_R32G32B32_SFLOAT; // vec3 vertex position data.
triangles.vertexData.deviceAddress = vertexAddress;
triangles.vertexStride = sizeof(nvmath::vec3f);
// 设置索引缓存(32比特无符号整形)
triangles.indexType = VK_INDEX_TYPE_UINT32;
triangles.indexData.deviceAddress = indexAddress;
// 底层加速结构本身的变换为单位矩阵(无变换)
//triangles.transformData = {};
triangles.maxVertex = prim.vertexCount - 1;

// 将之前的三角形设置为不透明
VkAccelerationStructureGeometryKHR asGeom{VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR};
asGeom.geometryType = VK_GEOMETRY_TYPE_TRIANGLES_KHR;
asGeom.flags = VK_GEOMETRY_NO_DUPLICATE_ANY_HIT_INVOCATION_BIT_KHR; // 对任意命中的限制
asGeom.geometry.triangles = triangles;

VkAccelerationStructureBuildRangeInfoKHR offset;
offset.firstVertex = prim.vertexOffset;
offset.primitiveCount = prim.indexCount / 3;
offset.primitiveOffset = prim.firstIndex * sizeof(uint32_t);
offset.transformOffset = 0;

// 目前我们一个底层加速结构对应一个几何体,但其实可以加入更多几何体
nvvk::RaytracingBuilderKHR::BlasInput input;
input.asGeometry.emplace_back(asGeom);
input.asBuildOffsetInfo.emplace_back(offset);

return input;
}

创建顶层加速结构
####################

基本上与之前的没什么区别,除了索引数据存在 ``primMesh`` 中。

.. code:: c++

for(auto& node : m_gltfScene.m_nodes)
{
VkAccelerationStructureInstanceKHR rayInst;
rayInst.transform = nvvk::toTransformMatrixKHR(node.worldMatrix);
rayInst.instanceCustomIndex = node.primMesh; // gl_InstanceCustomIndexEXT: 用于寻找图元
rayInst.accelerationStructureReference = m_rtBuilder.getBlasDeviceAddress(node.primMesh);
rayInst.flags = VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR;
rayInst.mask = 0xFF;
rayInst.instanceShaderBindingTableRecordOffset = 0; // 所有的物体都是用相同的命中组
tlas.emplace_back(rayInst);
}

.. admonition:: m_gltfScene.m_nodes
:class: note

长度为 ``10`` ,每个元素分别对应场景中的 ``10`` 个物体。



0 comments on commit 2a262fa

Please sign in to comment.