• Portfolio
  • Blog
  • About
  • Raw DirectX 12
  • Introduction
  • Initialize the API
  • Raster Graphics Pipeline
  • Compute Pipeline
  • Ray Tracing Pipeline
  • More Resources

Raw DirectX 12 Book

Author Pic:Alain Galvan ·12/28/2021 @ 7:36 PM · Updated 1 year ago

A comprehensive introduction to DirectX 12 Ultimate, learn how to render meshes, ray trace scenes, and learn the latest techniques and best practices from industry and research.

Ray Tracing in DirectX 12 brings hardware accelerated ray queries to the API, making it much easier and faster to calculate the radiance of a given scene for a path tracer, resolve more accurate reflections or global illumination, bake light maps or high poly geometry to textures, and even unique use cases like transforming audio for spatial post-processing effects.

Compiling Shaders

Ray Tracing Programmable Steps

DirectX Ray Tracing introduces 5 new programmable shader stages:

  • 🤾 Ray Generation - For creating starting rays, these could be rays from the eye of the viewport for instance.

  • 🗿 Intersection - A custom solution to handling hits, rather than relying on the vendor's ability to calculate intersections, you can programmatically report intersections for hits with a given ray.

  • 🥃 Any Hit - Any of the hits in a given ray, including hits far from the eye. Useful for thickness tests or translucent objects.

  • 🏏 Closest Hit - The earliest hit for a given ray, so the closest to the eye.

  • ⚾ Miss - If the ray fails to hit anything in the scene. Useful for adding contributions from your sky box.

// 🌎 Global Resources RaytracingAccelerationStructure Scene : register(t0, space0); RWTexture2D<float4> tOutput : register(u0); cbuffer globalCB : register(b0) { row_major float4x4 projectionMatrix : packoffset(c0); row_major float4x4 viewMatrix : packoffset(c4); float3 origin : packoffset(c8); float near : packoffset(c9.x); float far : packoffset(c9.y); }; struct RayPayload { float4 color; }; /** * 🤾 Ray Generation */ [shader("raygeneration")] void raygen() { float2 screenCoords = (float2)DispatchRaysIndex() / (float2)DispatchRaysDimensions(); float2 ndc = screenCoords * 2.0 - float2(1.0, 1.0); float3 rayDir = normalize(mul(mul(viewMatrix, projectionMatrix), float4(ndc * (far - near), far + near, far - near))).xyz; RayDesc ray; ray.Origin = origin.xyz; ray.Direction = rayDir; ray.TMin = 0.1; ray.TMax = 300.0; RayPayload payload = {float4(0, 0, 0, 0)}; TraceRay(Scene, RAY_FLAG_NONE, ~0, 0, 1, 0, ray, payload); tOutput[DispatchRaysIndex().xy] = payload.color; } /** * 🏏 Closest Hit */ // Local Resources struct LocalCB { float time; }; ConstantBuffer<LocalCB> localCB : register(b1); Texture2D<float4> localTex : register(t1); SamplerState localSampler : register(s0); [shader("closesthit")] void closesthit(inout RayPayload payload, in BuiltInTriangleIntersectionAttributes attr) { float3 barycentrics = float3(1 - attr.barycentrics.x - attr.barycentrics.y, attr.barycentrics.x, attr.barycentrics.y); float4 col = localTex.SampleLevel(localSampler, barycentrics.xy - barycentrics.yz * sin(localCB.time), 0.0); payload.color = col; } /** * ⚾ Miss */ [shader("miss")] void miss(inout RayPayload payload) { payload.color = float4(0.5, 0.5, 0.5, 1); }

Shaders can be compiled offline with DirectX Shader Compiler:

# 🔨 Run the DirectX Shader Compiler dxc.exe -T lib_6_3 -Fo assets/triangle.rt.dxil assets/triangle.rt.hlsl

Root Signature

Local and Global Root Signature Diagram

The Ray Tracing pipeline expects both a Local and Global root signature to describe what resources you can expect your shader to be able to read.

A Global Root Signature can be used for any shared data that you might want to read/write to across different dispatchRays, such as your acceleration structures or radiance buffer.

// 👋 Declare Handles ID3D12RootSignature* globalRootSignature = nullptr; // Global Root Signature // These can be configured prior to DispatchRays // Output radiance UAV D3D12_DESCRIPTOR_RANGE1 ranges[2]; ranges[0].BaseShaderRegister = 0; ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV; ranges[0].NumDescriptors = 1; ranges[0].RegisterSpace = 0; ranges[0].OffsetInDescriptorsFromTableStart = 0; ranges[0].Flags = D3D12_DESCRIPTOR_RANGE_FLAG_DATA_VOLATILE; // Camera Matrices CBV ranges[1].BaseShaderRegister = 0; ranges[1].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV; ranges[1].NumDescriptors = 1; ranges[1].RegisterSpace = 0; ranges[1].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; ranges[1].Flags = D3D12_DESCRIPTOR_RANGE_FLAG_DATA_VOLATILE | D3D12_DESCRIPTOR_RANGE_FLAG_DESCRIPTORS_VOLATILE; D3D12_ROOT_PARAMETER1 rootParameters[2]; // UAV, CBV rootParameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; rootParameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; rootParameters[0].DescriptorTable.NumDescriptorRanges = _countof(ranges); rootParameters[0].DescriptorTable.pDescriptorRanges = ranges; // Acceleration Structures rootParameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_SRV; rootParameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; rootParameters[1].Descriptor = {}; D3D12_VERSIONED_ROOT_SIGNATURE_DESC rootSignatureDesc; rootSignatureDesc.Version = D3D_ROOT_SIGNATURE_VERSION_1_1; rootSignatureDesc.Desc_1_1.Flags = D3D12_ROOT_SIGNATURE_FLAG_NONE; rootSignatureDesc.Desc_1_1.NumParameters = _countof(rootParameters); rootSignatureDesc.Desc_1_1.pParameters = rootParameters; rootSignatureDesc.Desc_1_1.NumStaticSamplers = 0; rootSignatureDesc.Desc_1_1.pStaticSamplers = nullptr; ID3DBlob* signature; ID3DBlob* error; try { ThrowIfFailed(D3D12SerializeVersionedRootSignature( &rootSignatureDesc, &signature, &error)); ThrowIfFailed(device->CreateRootSignature( 0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&globalRootSignature))); globalRootSignature->SetName(L"Global Root Signature"); } catch (std::exception e) { const char* errStr = (const char*)error->GetBufferPointer(); std::cout << errStr; error->Release(); error = nullptr; } if (signature) { signature->Release(); signature = nullptr; }

Local root signatures are unique in that each programmable step of ray tracing can define their own local root signatures.

// 👋 Declare Handles ID3D12RootSignature* localRootSignature = nullptr; // Local Root Signature // Shader tables are able to configure this root signature. D3D12_ROOT_PARAMETER1 rootParameters[2] = {}; // Viewport Misc Constant rootParameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; rootParameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS; rootParameters[0].Constants = {}; rootParameters[0].Constants.ShaderRegister = 1; rootParameters[0].Constants.Num32BitValues = ((sizeof(mRayGenCB) - 1) / sizeof(UINT32) + 1); // Texture Param SRV Table D3D12_DESCRIPTOR_RANGE1 ranges[1] = {}; ranges[0].BaseShaderRegister = 1; ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; ranges[0].NumDescriptors = 1; ranges[0].RegisterSpace = 0; ranges[0].OffsetInDescriptorsFromTableStart = 2; ranges[0].Flags = D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC; rootParameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; rootParameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; rootParameters[1].DescriptorTable.pDescriptorRanges = &ranges[0]; rootParameters[1].DescriptorTable.NumDescriptorRanges = _countof(ranges); D3D12_STATIC_SAMPLER_DESC sampler = {}; sampler.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT; sampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_BORDER; sampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_BORDER; sampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_BORDER; sampler.MipLODBias = 0; sampler.MaxAnisotropy = 0; sampler.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER; sampler.BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK; sampler.MinLOD = 0.0f; sampler.MaxLOD = D3D12_FLOAT32_MAX; sampler.ShaderRegister = 0; sampler.RegisterSpace = 0; sampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; D3D12_VERSIONED_ROOT_SIGNATURE_DESC rootSignatureDesc; rootSignatureDesc.Version = D3D_ROOT_SIGNATURE_VERSION_1_1; rootSignatureDesc.Desc_1_1.Flags = D3D12_ROOT_SIGNATURE_FLAG_LOCAL_ROOT_SIGNATURE; rootSignatureDesc.Desc_1_1.NumParameters = _countof(rootParameters); rootSignatureDesc.Desc_1_1.pParameters = rootParameters; rootSignatureDesc.Desc_1_1.NumStaticSamplers = 1; rootSignatureDesc.Desc_1_1.pStaticSamplers = &sampler; ID3DBlob* signature; ID3DBlob* error; try { ThrowIfFailed(D3D12SerializeVersionedRootSignature( &rootSignatureDesc, &signature, &error)); ThrowIfFailed(device->CreateRootSignature( 0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&localRootSignature))); localRootSignature->SetName(L"Local Root Signature"); } catch (std::exception e) { const char* errStr = (const char*)error->GetBufferPointer(); std::cout << errStr; error->Release(); error = nullptr; } if (signature) { signature->Release(); signature = nullptr; }

Shader Table

Shader Table Diagram

Shader Record Tables are 64 bit aligned (D3D12_RAYTRACING_SHADER_TABLE_BYTE_ALIGNMENT) lists of shader records, lists that contain an identifier and that shader's local root arguments from the local root signature. These root arguments could be the entirety of a constant buffer's data, or descriptor handles to textures, buffers, unordered access views, etc.

struct LocalCB { float time; float _padding[3]; }; struct RootArguments { LocalCB localCB; D3D12_GPU_DESCRIPTOR_HANDLE localTex = {}; }; struct ShaderRecord { unsigned char shaderIdentifier[D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES]; RootArguments rootArguments; };

Pipeline State

Ray Tracing Pipeline Diagram

The Ray Tracing Pipeline is a a hardware accelerated way of traversing acceleration data structures to find objects in a given scene. DirectX Ray Tracing adopts a more declarative Pipeline State description layout, rather than a strict layout like with the raster or compute pipelines, where you define sub-objects with any information you might need.

You will need at least the following to create a Ray Tracing Pipeline:

  • 📚 A DXIL Shader Library with compiled shaders you'll be using when dispatching rays.

  • 💰 A Shader Configuration to define how large your hit payloads should be for intersection/misses.

  • 🔁 A Pipeline Configuration to define the level of recursion depth for traversing rays.

  • 📑 Triangle Hit Groups to store hit information.

  • 🇺🇸 A Local Root Signature for local resources.

  • 🌎 A Global Root Signature for global resources such as your RWTexture<float4> radiance buffer.

// 👋 Declare handles ID3D12PipelineState* pipelineState; ID3D12StateObjectProperties* stateObjectProperties; // 🗺️ Create the State Object Description D3D12_STATE_OBJECT_DESC stateObjectDesc = {}; stateObjectDesc.Type = D3D12_STATE_OBJECT_TYPE_RAYTRACING_PIPELINE; D3D12_STATE_SUBOBJECT subObjects[7]; stateObjectDesc.NumSubobjects = _countof(subObjects); stateObjectDesc.pSubobjects = subObjects; D3D12_EXPORT_DESC* exportDesc = nullptr; D3D12_SHADER_BYTECODE rtShaderByteCode = {rsBytecodeData.data(), rsBytecodeData.size()}; // 📚 Shader Library D3D12_DXIL_LIBRARY_DESC dxilLibraryDesc = {rtShaderByteCode, 0u, exportDesc}; subObjects[0u] = {D3D12_STATE_SUBOBJECT_TYPE_DXIL_LIBRARY, &dxilLibraryDesc}; // 📕 Active Shaders static LPCWSTR shaderNames[3] = { L"raygen", L"closesthit", L"miss"}; D3D12_SUBOBJECT_TO_EXPORTS_ASSOCIATION exportAssociations = { subObjects + 4, 3u, shaderNames}; subObjects[1] = { D3D12_STATE_SUBOBJECT_TYPE_SUBOBJECT_TO_EXPORTS_ASSOCIATION, &exportAssociations}; // 💰 Payload Setup D3D12_RAYTRACING_SHADER_CONFIG shaderConfig = {}; shaderConfig.MaxPayloadSizeInBytes = 4 * sizeof(float); // float4 color shaderConfig.MaxAttributeSizeInBytes = 2 * sizeof(float); // float2 barycentrics subObjects[2] = {D3D12_STATE_SUBOBJECT_TYPE_RAYTRACING_SHADER_CONFIG, &shaderConfig}; // 📑 Hit Groups D3D12_HIT_GROUP_DESC myClosestHitGroup = { L"MyHitGroup", D3D12_HIT_GROUP_TYPE_TRIANGLES, nullptr, L"closesthit", nullptr}; subObjects[3] = {D3D12_STATE_SUBOBJECT_TYPE_HIT_GROUP, &myClosestHitGroup}; // 🚃 Root Signatures subObjects[4] = {D3D12_STATE_SUBOBJECT_TYPE_LOCAL_ROOT_SIGNATURE, &localRootSignature}; subObjects[5] = {D3D12_STATE_SUBOBJECT_TYPE_GLOBAL_ROOT_SIGNATURE, &globalRootSignature}; // 🔁 Recursion Depth D3D12_RAYTRACING_PIPELINE_CONFIG pipelineConfig = {1u}; pipelineConfig.MaxTraceRecursionDepth = 1; subObjects[6] = {D3D12_STATE_SUBOBJECT_TYPE_RAYTRACING_PIPELINE_CONFIG, &pipelineConfig}; HRESULT result = device->CreateStateObject( &stateObjectDesc, __uuidof(**(&pipelineState)), IID_PPV_ARGS_Helper(&pipelineState)); pipelineState->SetName(L"RT Pipeline State"); pipelineState->QueryInterface( __uuidof(**(&stateObjectProperties)), IID_PPV_ARGS_Helper(&stateObjectProperties));

Acceleration Structures

Acceleration Structure Diagram

Acceleration Data Structures (or Acceleration Structures / AS) speed up the traversal of a given scene, and represent the internal acceleration data structure used by a hardware vendor to traverse a set of triangles or axis aligned boundary boxes (AABBs).

Scratch Buffers contain intermediary data that's used to create the Acceleration Structure. Scratch buffer memory points to vendor implementation specific acceleration structure state.

// 👋 Declare Handles ID3D12Device5* device; ID3D12Resource* vertexBuffer; ID3D12Resource* indexBuffer; ID3D12GraphicsCommandList4* commandList; // Declare Outputs ID3D12Resource* asBuffer; ID3D12Resource* asScratchBuffer; ID3D12Resource* instanceDescs; HRESULT hr; // 🌳 Top Level Acceleration Structure D3D12_RAYTRACING_GEOMETRY_DESC geomDescs[1]; geomDescs[0].Type = D3D12_RAYTRACING_GEOMETRY_TYPE_TRIANGLES; geomDescs[0].Triangles.IndexBuffer = indexBuffer->GetGPUVirtualAddress(); geomDescs[0].Triangles.IndexCount = static_cast<UINT>(indexBuffer->GetDesc().Width) / sizeof(UINT16); geomDescs[0].Triangles.IndexFormat = DXGI_FORMAT_R16_UINT; geomDescs[0].Triangles.Transform3x4 = 0; geomDescs[0].Triangles.VertexFormat = DXGI_FORMAT_R32G32B32_FLOAT; geomDescs[0].Triangles.VertexCount = static_cast<UINT>(vertexBuffer->GetDesc().Width) / sizeof(Vertex); geomDescs[0].Triangles.VertexBuffer.StartAddress = vertexBuffer->GetGPUVirtualAddress(); geomDescs[0].Triangles.VertexBuffer.StrideInBytes = sizeof(Vertex); // 🧱 Get prebuild info D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS topLevelInputs[1]; topLevelInputs[0].DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY; topLevelInputs[0].Flags = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAG_PREFER_FAST_TRACE; topLevelInputs[0].NumDescs = 1; topLevelInputs[0].Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL; D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO topLevelPrebuildInfo = {}; device->GetRaytracingAccelerationStructurePrebuildInfo(topLevelInputs, topLevelPrebuildInfo); D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS bottomLevelInputs[1]; bottomLevelInputs[0] = topLevelInputs[0]; bottomLevelInputs.Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL; bottomLevelInputs.pGeometryDescs = geomDescs; D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO bottomLevelPrebuildInfo = {}; device->GetRaytracingAccelerationStructurePrebuildInfo( &bottomLevelInputs, &bottomLevelPrebuildInfo); // 🍱 Create Scratch Buffer D3D12_RESOURCE_DESC asResourceDesc; asResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; asResourceDesc.Alignment = 0; asResourceDesc.Width = max(topLevelPrebuildInfo.ScratchDataSizeInBytes, bottomLevelPrebuildInfo.ScratchDataSizeInBytes); asResourceDesc.Height = 1; asResourceDesc.DepthOrArraySize = 1; asResourceDesc.MipLevels = 1; asResourceDesc.Format = DXGI_FORMAT_UNKNOWN; asResourceDesc.SampleDesc.Count = 1; asResourceDesc.SampleDesc.Quality = 0; asResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; asResourceDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; // 🍱 Create AS Scratch Buffer hr = device->CreateCommittedResource( &heapProps, D3D12_HEAP_FLAG_NONE, &asResourceDesc, D3D12_RESOURCE_STATE_RAYTRACING_ACCELERATION_STRUCTURE, nullptr, IID_PPV_ARGS(&asScratchBuffer)); // 🍱 Create TLAS Buffer asResourceDesc.width = topLevelPrebuildInfo.ResultDataMaxSizeInBytes; hr = device->CreateCommittedResource( &heapProps, D3D12_HEAP_FLAG_NONE, &asResourceDesc, D3D12_RESOURCE_STATE_RAYTRACING_ACCELERATION_STRUCTURE, nullptr, IID_PPV_ARGS(&tlasBuffer)); // 🍱 Create BLAS Buffer asResourceDesc.width = bottomLevelPrebuildInfo.ResultDataMaxSizeInBytes; hr = device->CreateCommittedResource( &heapProps, D3D12_HEAP_FLAG_NONE, &asResourceDesc, D3D12_RESOURCE_STATE_RAYTRACING_ACCELERATION_STRUCTURE, nullptr, IID_PPV_ARGS(&blasBuffer)); D3D12_RAYTRACING_INSTANCE_DESC instanceDesc = {}; instanceDesc.Transform[0][0] = instanceDesc.Transform[1][1] = instanceDesc.Transform[2][2] = 1; instanceDesc.InstanceMask = 1; instanceDesc.AccelerationStructure = blasBuffer->GetGPUVirtualAddress(); D3D12_HEAP_PROPERTIES uploadHeapProperties = {}; uploadHeapProperties.Type = D3D12_HEAP_TYPE_UPLOAD; uploadHeapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; uploadHeapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; uploadHeapProperties.CreationNodeMask = 1; uploadHeapProperties.VisibleNodeMask = 1; D3D12_RESOURCE_DESC bufferDesc = {}; bufferDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; bufferDesc.Alignment = 0; bufferDesc.Width = sizeof(instanceDesc); bufferDesc.Height = 1; bufferDesc.DepthOrArraySize = 1; bufferDesc.MipLevels = 1; bufferDesc.Format = DXGI_FORMAT_UNKNOWN; bufferDesc.SampleDesc.Count = 1; bufferDesc.SampleDesc.Quality = 0; bufferDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; bufferDesc.Flags = D3D12_RESOURCE_FLAG_NONE; hr = device->CreateCommittedResource( &uploadHeapProperties, D3D12_HEAP_FLAG_NONE, &bufferDesc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(instanceDescs)); void* pMappedData; (*instanceDescs)->Map(0, nullptr, &pMappedData); memcpy(pMappedData, &instanceDesc, sizeof(instanceDesc)); (*instanceDescs)->Unmap(0, nullptr); // 🏗️ BLAS build command D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC bottomLevelBuildDesc = {}; bottomLevelBuildDesc.Inputs = bottomLevelInputs; bottomLevelBuildDesc.ScratchAccelerationStructureData = scratchResource->GetGPUVirtualAddress(); bottomLevelBuildDesc.DestAccelerationStructureData = blasBuffer->GetGPUVirtualAddress(); // 🏗️ TLAS build command D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC topLevelBuildDesc = {}; topLevelInputs.InstanceDescs = instanceDescs->GetGPUVirtualAddress(); topLevelBuildDesc.Inputs = topLevelInputs; topLevelBuildDesc.DestAccelerationStructureData = tlasBuffer->GetGPUVirtualAddress(); topLevelBuildDesc.ScratchAccelerationStructureData = scratchResource->GetGPUVirtualAddress(); // 🏢 Execute build commandList->BuildRaytracingAccelerationStructure(&bottomLevelBuildDesc, 0, nullptr); D3D12_RESOURCE_BARRIER barrier = {}; barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV; barrier.UAV.pResource = blasBuffer; commandList->ResourceBarrier(1, &barrier); commandList->BuildRaytracingAccelerationStructure(&topLevelBuildDesc, 0, nullptr);

Commands

Ray Tracing Commands Diagram

The ray tracing pipeline is similar to the compute pipeline, you dispatchRays over a given kernel size rather then groups as in traditional compute dispatch, so it's a lot easier to envision, for example as a set of rays for each pixel of your radiance buffer.

ThrowIfFailed(commandAllocator->Reset()); ThrowIfFailed(commandList->Reset(commandAllocator, nullptr)); commandList->SetComputeRootSignature(globalRootSignature); // 🌎 Bind global root signature descriptor data commandList->SetDescriptorHeaps(1, &srvHeap); commandList->SetComputeRootDescriptorTable(0, srvHeap->GetGPUDescriptorHandleForHeapStart()); // 🚞 Bind TLAS commandList->SetComputeRootShaderResourceView( 1, tlas->GetGPUVirtualAddress()); // ⚡ Setup dispatch description D3D12_DISPATCH_RAYS_DESC dispatchDesc = {}; dispatchDesc.Width = width; dispatchDesc.Height = height; dispatchDesc.Depth = 1; // 🏓 Shader Binding Tables dispatchDesc.RayGenerationShaderRecord.StartAddress = rayGenShaderTable->GetGPUVirtualAddress(); dispatchDesc.RayGenerationShaderRecord.SizeInBytes = rayGenShaderTable->GetDesc().Width; dispatchDesc.MissShaderTable.StartAddress = missShaderTable->GetGPUVirtualAddress(); dispatchDesc.MissShaderTable.SizeInBytes = missShaderTable->GetDesc().Width; dispatchDesc.MissShaderTable.StrideInBytes = dispatchDesc.MissShaderTable.SizeInBytes; dispatchDesc.HitGroupTable.StartAddress = hitShaderTable->GetGPUVirtualAddress(); dispatchDesc.HitGroupTable.SizeInBytes = hitShaderTable->GetDesc().Width; dispatchDesc.HitGroupTable.StrideInBytes = dispatchDesc.HitGroupTable.SizeInBytes; // 👨‍🔧 Setup Pipeline commandList->SetPipelineState1(pipelineState); // 🏁 Dispatch Rays commandList->DispatchRays(&dispatchDesc);

More Resources

  • Ray tracing is a complex topic with tons of prior art to it. Among some of the most important and interesting topics are:

    • Probability Distributions - how to approach increasing the probability of a given behavior in monte-carlo integration. For instance, you may want multiple importance sampling to help with calculating irradiance/radiance in the scene.
  • The DirectX 12 Ray Tracing Specification is a comprehensive resource that describes some of the motivations and design of the API.

  • AMD GPUOpen released a guide to improving ray tracing performance with the Radeon Ray Tracing Analyzer.

Tags: booksopengltrianglehellotheorydiagramsdirectxvulkan

GitHub Comments