# DXR Tutorial Extra : Indexed Geometry Welcome to the next section of the tutorial. If you miss the first tutorial, it is [here](/rtx/raytracing/dxr/DX12-Raytracing-tutorial-Part-1) The bases of this tutorial starts at the end of the previous one. You can download the entire project [here](/rtx/raytracing/dxr/tutorial/Files/dxr_tutorial.zip) The first tutorial only shows a triangle, which can feel a bit simplistic: ![](https://d29g4g2dyqv443.cloudfront.net/sites/default/files/pictures/2018/dx12_rtx_tutorial/Extra/originalRender.png) In this tutorial, we will convert the plane triangle to a three dimensional one, a tetrahedron. Do do this, we will convert the simple triangle to an indexed version of it. Add the new resources ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ComPtr m_indexBuffer; D3D12_INDEX_BUFFER_VIEW m_indexBufferView; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ## LoadAssets Instead of a simple triangle, let's create a [tetrahedron](https://en.wikipedia.org/wiki/Tetrahedron), which requires 4 vertices. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Vertex triangleVertices[] = { {{std::sqrtf(8.f / 9.f), 0.f, -1.f / 3.f}, {1.f, 0.f, 0.f, 1.f}}, {{-std::sqrtf(2.f / 9.f), std::sqrtf(2.f / 3.f), -1.f / 3.f}, {0.f, 1.f, 0.f, 1.f}}, {{-std::sqrtf(2.f / 9.f), -std::sqrtf(2.f / 3.f), -1.f / 3.f}, {0.f, 0.f, 1.f, 1.f}}, {{0.f, 0.f, 1.f}, {1, 0, 1, 1}}}; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Then, we need to create and set the indices right after setting `m_vertexBufferView`. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //---------------------------------------------------------------------------------------------- // Indices std::vector indices = {0, 1, 2, 0, 3, 1, 0, 2, 3, 1, 3, 2}; const UINT indexBufferSize = static_cast(indices.size()) * sizeof(UINT); CD3DX12_HEAP_PROPERTIES heapProperty = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD); CD3DX12_RESOURCE_DESC bufferResource = CD3DX12_RESOURCE_DESC::Buffer(indexBufferSize); ThrowIfFailed(m_device->CreateCommittedResource( &heapProperty, D3D12_HEAP_FLAG_NONE, &bufferResource, // D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&m_indexBuffer))); // Copy the triangle data to the index buffer. UINT8* pIndexDataBegin; ThrowIfFailed(m_indexBuffer->Map(0, &readRange, reinterpret_cast(&pIndexDataBegin))); memcpy(pIndexDataBegin, indices.data(), indexBufferSize); m_indexBuffer->Unmap(0, nullptr); // Initialize the index buffer view. m_indexBufferView.BufferLocation = m_indexBuffer->GetGPUVirtualAddress(); m_indexBufferView.Format = DXGI_FORMAT_R32_UINT; m_indexBufferView.SizeInBytes = indexBufferSize; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ## PopulateCommandList To draw the tetrahedron in the raster, you simply need to change how it is draw, by making the following changes in `PopulateCommandList()`. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ m_commandList->IASetVertexBuffers(0, 1, &m_vertexBufferView); m_commandList->IASetIndexBuffer(&m_indexBufferView); m_commandList->DrawIndexedInstanced(12, 1, 0, 0, 0); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The result image is not great an will be quite flat ![](https://d29g4g2dyqv443.cloudfront.net/sites/default/files/pictures/2018/dx12_rtx_tutorial/Extra/tetra_flat.png) ## CreateBottomLevelAS To see this geometry in the raytracing path, we need to improve the `CreateBottomLevelAS` method to support indexed geometry. We first change the signature of the method to include index buffers: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C AccelerationStructureBuffers CreateBottomLevelAS( std::vector<:pair>, uint32_t>> vVertexBuffers, std::vector<:pair>, uint32_t>> vIndexBuffers = {}); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ We then replace the beginning of the method as follows so that the bottom-level AS helper is called with indexing if needed: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // #DXR Extra: Indexed Geometry D3D12HelloTriangle::AccelerationStructureBuffers D3D12HelloTriangle::CreateBottomLevelAS( std::vector<:pair>, uint32_t>> vVertexBuffers, std::vector<:pair>, uint32_t>> vIndexBuffers) { nv_helpers_dx12::BottomLevelASGenerator bottomLevelAS; // Adding all vertex buffers and not transforming their position. for (size_t i = 0; i < vVertexBuffers.size(); i++) { // for (const auto &buffer : vVertexBuffers) { if (i < vIndexBuffers.size() && vIndexBuffers[i].second > 0) bottomLevelAS.AddVertexBuffer(vVertexBuffers[i].first.Get(), 0, vVertexBuffers[i].second, sizeof(Vertex), vIndexBuffers[i].first.Get(), 0, vIndexBuffers[i].second, nullptr, 0, true); else bottomLevelAS.AddVertexBuffer(vVertexBuffers[i].first.Get(), 0, vVertexBuffers[i].second, sizeof(Vertex), 0, 0); } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ## CreateAccelerationStructures The acceleration structure build calls also need to be updated to reflect the new interface as well as to add the new geometry: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Build the bottom AS from the Triangle vertex buffer AccelerationStructureBuffers bottomLevelBuffers = CreateBottomLevelAS({{m_vertexBuffer.Get(), 4}}, {{m_indexBuffer.Get(), 12}}); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ But the shading is not correct with the raytracer. This is because we are accessing invalid data in the Hit Shader. ![Shading issue](https://d29g4g2dyqv443.cloudfront.net/sites/default/files/pictures/2018/dx12_rtx_tutorial/Extra/tetra_rt_issue.png width=400) ## Hit Shader In the hit shader, we will need to access the indices ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ StructuredBuffer indices: register(t1); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Then in the shader access the right vertex ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ float3 hitColor = BTriVertex[indices[vertId + 0]].color * barycentrics.x + BTriVertex[indices[vertId + 1]].color * barycentrics.y + BTriVertex[indices[vertId + 2]].color * barycentrics.z; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ## CreateHitSignature Changing the shader is not enough, we need to inform the shader that more information is needed. Do to this, change the signature in `CreateHitSignature`. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ rsc.AddRootParameter(D3D12_ROOT_PARAMETER_TYPE_SRV, 0 /*t0*/); // vertices and colors rsc.AddRootParameter(D3D12_ROOT_PARAMETER_TYPE_SRV, 1 /*t1*/); // indices ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ## CreateShaderBindingTable Finally, we need to bind the new data to the shader and we are doing it in the `CreateShaderBindingTable` by modifying the data pass to the `HitGroup`. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ m_sbtHelper.AddHitGroup(L"HitGroup", {(void*)(m_vertexBuffer->GetGPUVirtualAddress()), (void*)(m_indexBuffer->GetGPUVirtualAddress())}); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Now the result in the raytracer is similar to the rasterizer. Note that if you have other hit groups attached to the same root signature, you would have to adjust their list of root parameters as well. ![](https://d29g4g2dyqv443.cloudfront.net/sites/default/files/pictures/2018/dx12_rtx_tutorial/Extra/tetra_rt_flat.png) # Perspective & Depth & Plane An orthographic view and single geometry does not help seeing the geometry right. Follow the tutorials: * [Perspective](/rtx/raytracing/dxr/DX12-Raytracing-tutorial/Extra/dxr_tutorial_extra_perspective): Adding a camera perspective * [Instance Data](/rtx/raytracing/dxr/DX12-Raytracing-tutorial/Extra/dxr_tutorial_extra_per_instance_data): Adding a plane * [Depth Buffer](/rtx/raytracing/dxr/DX12-Raytracing-tutorial/Extra/dxr_tutorial_extra_depth_buffer): Adding the depth buffer to the raster This will give following result: ![](https://d29g4g2dyqv443.cloudfront.net/sites/default/files/pictures/2018/dx12_rtx_tutorial/Extra/tetra_rt_3d.png) # Menger Sponge fractal In the following part of the tutorial we will add some more complex geometry, using indexed vertex buffers. The geometry itself is a randomized variation of the Menger Sponge fractal. Add the following declarations in the header file: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // #DXR Extra: Indexed Geometry void CreateMengerSpongeVB(); ComPtr< ID3D12Resource > m_mengerVB; ComPtr< ID3D12Resource > m_mengerIB; D3D12_VERTEX_BUFFER_VIEW m_mengerVBView; D3D12_INDEX_BUFFER_VIEW m_mengerIBView; UINT m_mengerIndexCount; UINT m_mengerVertexCount; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The procedural generation code is provided in the `DXRHelpers.h`. This function also provides normal information, that we will not use in this tutorial. For compatibility, add the following constructors to the `Vertex` structure: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // #DXR Extra: Indexed Geometry Vertex(XMFLOAT4 pos, XMFLOAT4 /*n*/, XMFLOAT4 col) :position(pos.x, pos.y, pos.z), color(col) {} Vertex(XMFLOAT3 pos, XMFLOAT4 col) :position(pos), color(col) {} ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ At the end of the source file, add the implementation of the creation of the geometry buffers for the Menger Sponge. This method creates a vertex buffer and an index buffer, as well as views on those buffers for later use in the rasterization path. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // #DXR Extra: Indexed Geometry void D3D12HelloTriangle::CreateMengerSpongeVB() { std::vector< Vertex > vertices; std::vector< UINT > indices; nv_helpers_dx12::GenerateMengerSponge(3, 0.75, vertices, indices); { const UINT mengerVBSize = static_cast(vertices.size()) * sizeof(Vertex); // Note: using upload heaps to transfer static data like vert buffers is not // recommended. Every time the GPU needs it, the upload heap will be // marshalled over. Please read up on Default Heap usage. An upload heap is // used here for code simplicity and because there are very few verts to // actually transfer. CD3DX12_HEAP_PROPERTIES heapProperty = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD); CD3DX12_RESOURCE_DESC bufferResource = CD3DX12_RESOURCE_DESC::Buffer(mengerVBSize); ThrowIfFailed(m_device->CreateCommittedResource( &heapProperty, D3D12_HEAP_FLAG_NONE, &bufferResource, // D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&m_mengerVB))); // Copy the triangle data to the vertex buffer. UINT8* pVertexDataBegin; CD3DX12_RANGE readRange(0, 0); // We do not intend to read from this resource on the CPU. ThrowIfFailed(m_mengerVB->Map(0, &readRange, reinterpret_cast(&pVertexDataBegin))); memcpy(pVertexDataBegin, vertices.data(), mengerVBSize); m_mengerVB->Unmap(0, nullptr); // Initialize the vertex buffer view. m_mengerVBView.BufferLocation = m_mengerVB->GetGPUVirtualAddress(); m_mengerVBView.StrideInBytes = sizeof(Vertex); m_mengerVBView.SizeInBytes = mengerVBSize; } { const UINT mengerIBSize = static_cast(indices.size()) * sizeof(UINT); // Note: using upload heaps to transfer static data like vert buffers is not // recommended. Every time the GPU needs it, the upload heap will be // marshalled over. Please read up on Default Heap usage. An upload heap is // used here for code simplicity and because there are very few verts to // actually transfer. CD3DX12_HEAP_PROPERTIES heapProperty = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD); CD3DX12_RESOURCE_DESC bufferResource = CD3DX12_RESOURCE_DESC::Buffer(mengerIBSize); ThrowIfFailed(m_device->CreateCommittedResource( &heapProperty, D3D12_HEAP_FLAG_NONE, &bufferResource, // D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&m_mengerIB))); // Copy the triangle data to the index buffer. UINT8* pIndexDataBegin; CD3DX12_RANGE readRange(0, 0); // We do not intend to read from this resource on the CPU. ThrowIfFailed(m_mengerIB->Map(0, &readRange, reinterpret_cast(&pIndexDataBegin))); memcpy(pIndexDataBegin, indices.data(), mengerIBSize); m_mengerIB->Unmap(0, nullptr); // Initialize the index buffer view. m_mengerIBView.BufferLocation = m_mengerIB->GetGPUVirtualAddress(); m_mengerIBView.Format = DXGI_FORMAT_R32_UINT; m_mengerIBView.SizeInBytes = mengerIBSize; m_mengerIndexCount = static_cast(indices.size()); m_mengerVertexCount = static_cast(vertices.size()); } } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ## LoadAssets Call the geometry generation method right after the initialization of `m_vertexBufferView`: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // #DXR Extra: Indexed Geometry CreateMengerSpongeVB(); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ## PopulateCommandList We can now draw the geometry in the raster path, by adding the draw calls right after drawing the triangle: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // #DXR Extra: Indexed Geometry // In a way similar to triangle rendering, rasterize the Menger Sponge m_commandList->IASetVertexBuffers(0, 1, &m_mengerVBView); m_commandList->IASetIndexBuffer(&m_mengerIBView); m_commandList->DrawIndexedInstanced(m_mengerIndexCount, 1, 0, 0, 0); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ## CreateAccelerationStructures The acceleration structure build calls also need to be updated to reflect the new interface as well as to add the new geometry: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // #DXR Extra: Indexed Geometry // Build the bottom AS from the Menger Sponge vertex buffer // #DXR Extra: Indexed Geometry // Build the bottom AS from the Menger Sponge vertex buffer AccelerationStructureBuffers mengerBottomLevelBuffers = CreateBottomLevelAS({{m_mengerVB.Get(), m_mengerVertexCount}}, {{m_mengerIB.Get(), m_mengerIndexCount}}); // Add both the triangle and the indexed geometry m_instances = { {bottomLevelBuffers.pResult, XMMatrixIdentity()}, { mengerBottomLevelBuffers.pResult, XMMatrixIdentity() } }; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ## CreateShaderBindingTable We shouldn't forget to add the binding of the new instance in `CreateShaderBindingTable` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ m_sbtHelper.AddHitGroup(L"HitGroup", {(void*)(m_mengerVB->GetGPUVirtualAddress()), (void*)(m_mengerIB->GetGPUVirtualAddress())}); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The geometry is now visible in both rasterization and raytracing: ![Raster](https://d29g4g2dyqv443.cloudfront.net/sites/default/files/pictures/2018/dx12_rtx_tutorial/Extra/mengerSpongePerspectiveRaster.png) ![Raytracing](https://d29g4g2dyqv443.cloudfront.net/sites/default/files/pictures/2018/dx12_rtx_tutorial/Extra/mengerSpongePerspectiveRaytracing.png)