Trazado de rayos DirectX: ventana emergente



En 2018, Microsoft anunció la API de trazado de rayos (DXR) como parte de DirectX 12. El enfoque de trazado de rayos obliga a un replanteamiento completo de la forma en que se renderizan las escenas 3D, cambiando el enfoque de rasterización clásico a un segundo plano. Se están modernizando las API, se están desarrollando GPU más eficientes y los desarrolladores de paquetes de visualización están probando nuevas posibilidades. Sin embargo, incluso en las tarjetas gráficas más potentes, la potencia es suficiente para generar solo unos pocos haces por píxel para garantizar una velocidad de cuadro estable. Además, el rendimiento depende en gran medida de la complejidad de los materiales y la escena. Pero incluso hoy en día, los algoritmos avanzados de reducción de ruido y acumulación del resultado de iluminación permiten lograr un alto grado de realismo. Todo esto motiva a experimentar en esta área.



El trazado de rayos GPU es relativamente nuevo. En 2009, DirectX 11 salió con los sombreadores de cómputo, lo que impulsó el desarrollo de la computación no gráfica. Sin embargo, el diseño de las estructuras de aceleración recayó completamente sobre los hombros del programador, lo que ralentizó el desarrollo. Se han extendido bibliotecas de intersecciones especializadas, como las Radeon Rays de AMD. En DXR, las estructuras de aceleración se representan mediante un principio de caja negra y la intersección se realiza mediante bloques de hardware especiales. El trazado de rayos también se ha agregado a Vulkan como una extensión VK_NV_ray_tracing para tarjetas Nvidia. En marzo de 2020, con cambios menores, se lanzaron las extensiones VK_KHR_ray_tracing, las específicas del proveedor dejaron de serlo, pueden incluirse en la especificación Vulkan 1.3.El trazado de rayos completo está previsto para AMD a finales de 2020. El soporte ubicuo hace que la tecnología sea más prometedora.



DXR . — . , . , , , . , (, ). , (hit, miss, procedural hit, closest hit), , , , . pipeline .





DXR- GPU Nvidia RTX 2060 . Windows SDK 19041 ( , Miscrosoft ), IDE Visual Studio 2019, C++.





DXR , raygen-. ID3D12GraphicsCommandList4::DispatchRays() c , TraceRay(). top level acceleration structure. , . , , , (closest hit). , .





miss hit-. payload — . DirectX, global root signature. per-shader , local root signature Shader binding table. , .





:



  • bottom-level, top-level acceleration structures
  • raytracing pipeline
  • shading binding table (SBT) — per-shader


.



Bottom-level acceleration structure (BLAS)



, — . BLAS . BLAS . . BLAS GPU, command list . , BLAS (scratch-). DirectX 12, API, ID3D12Device5::GetRaytracingAccelerationStructurePrebuildInfo. D3D12_RESOURCE_STATE_RAYTRACING_ACCELERATION_STRUCTURE — .



D3D12_RAYTRACING_GEOMETRY_DESC — BLAS. ( index-buffer ):



D3D12_RAYTRACING_GEOMETRY_DESC geometryDesc = {};
geometryDesc.Type = D3D12_RAYTRACING_GEOMETRY_TYPE_TRIANGLES;
geometryDesc.Triangles.IndexBuffer = 0;// GPU-address index- (  )
geometryDesc.Triangles.IndexCount = 0;
geometryDesc.Triangles.IndexFormat = 0;//  DXGI_FORMAT_R16_UINT;
geometryDesc.Triangles.VertexFormat = DXGI_FORMAT_R32G32B32_FLOAT;
geometryDesc.Triangles.VertexCount = < >;
geometryDesc.Triangles.VertexBuffer.StartAddress = <GPU-address  >;
geometryDesc.Triangles.VertexBuffer.StrideInBytes = sizeof(Vertex);


BLAS:



D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS bottomLevelInputs;
bottomLevelInputs.DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY;
bottomLevelInputs.Flags = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAG_PREFER_FAST_TRACE;
bottomLevelInputs.Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL;
bottomLevelInputs.pGeometryDescs = &geometryDesc;
bottomLevelInputs.NumDescs = 1;

D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC bottomLevelBuildDesc = {};
bottomLevelBuildDesc.Inputs = bottomLevelInputs;
bottomLevelBuildDesc.ScratchAccelerationStructureData = scratchResource->GetGPUVirtualAddress();
bottomLevelBuildDesc.DestAccelerationStructureData = bottomLevelAccelerationStructure->GetGPUVirtualAddress(); // ID3D12Resource* bottomLevelAccelerationStructure -    


BLAS:



dxrCommandList->BuildRaytracingAccelerationStructure(&bottomLevelBuildDesc, 0, nullptr);


Top-level acceleration structure (TLAS)



TLAS BLAS . BLAS , . TLAS BLAS — scratch-. , scratch-, TLAS BLAS. TLAS :



    D3D12_RAYTRACING_INSTANCE_DESC instanceDesc[2] = {};

    for (int i = 0; i < 2; ++i)
    {
        instanceDesc[i].Transform = YourEngineTransformToDXR(transforms[i]);
        instanceDesc[i].InstanceMask = 1;
        instanceDesc[i].InstanceID = 0; //  
        instanceDesc[i].InstanceContributionToHitGroupIndex = i; //     hit SBT
        instanceDesc[i].AccelerationStructure = bottomLevelAccelerationStructure->GetGPUVirtualAddress();
    }


TLAS (D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC) BLAS.



Raytracing pipeline



graphic compute raytracing pipeline state object — . RT pipeline -, . CD3DX12_STATE_OBJECT_DESC CreateSubobject(< >).



  1. . BytecodeLength pShaderBytecode D3D12_SHADER_BYTECODE, SetDXILLibrary() DefineExport(). 3- : raygen, hit, miss.
  2. Hit group (CD3DX12_HIT_GROUP_SUBOBJECT). hit group-, . SetHitGroupExport, SetHitGroupType(D3D12_HIT_GROUP_TYPE_TRIANGLES). hit-group, .
  3. (CD3DX12_RAYTRACING_SHADER_CONFIG_SUBOBJECT). payload — , TraceRay , closest hit — 2 float.
  4. (CD3DX12_RAYTRACING_PIPELINE_CONFIG_SUBOBJECT). 1.
  5. root signature (CD3DX12_LOCAL_ROOT_SIGNATURE_SUBOBJECT). , hit-. SBT ( ).
  6. local root signatore c hit group (CD3DX12_SUBOBJECT_TO_EXPORTS_ASSOCIATION_SUBOBJECT). root signature SetSubobjectToAssociate() AddExport().
  7. root signature (CD3DX12_GLOBAL_ROOT_SIGNATURE_SUBOBJECT). , . SetComputeRootDescriptorTable, SetComputeRootShaderResourceView SetComputeRoot32BitConstants.


Shader binding table (SBT)



SBT — . , , . , GPU-, . .



shader record. TLAS , — . . TLAS BLAS, TraceRay(), .



3 : ray generation, hit miss, , , . , . GPU- , DispatchRays(). shader record-.



shader record-a . raygen miss , shader record-a . , , , root signature, SBT. shader record D3D12_RAYTRACING_SHADER_RECORD_BYTE_ALIGNMENT ( 32 ).



void*, GetShaderIdentifier() ID3D12StateObjectProperties, COM- raytracing pipeline. raygen- (miss ) :



ComPtr<ID3D12StateObjectProperties> stateObjectProperties;
dxrStateObject.As(&stateObjectProperties);
void* rayGenShaderIdentifier = stateObjectProperties->GetShaderIdentifier(L"RayGen");


. SBT raygen miss .



hit float4 . SBT root signature: virtual GPU-address/GPU-handle. constant buffer, hit SBT shader record-. 32- 4x4- , 48, - 64 . :







vertex-fragment shader 3: raygen, hit, miss.



raygen:



#include "Common.hlsl"
#include "Global.hlsl"

struct CameraData
{
    float4 forward;
    float4 right;
    float4 up;
    float4 origin;
};

ConstantBuffer<CameraData> gCamera : register(b0);

[shader("raygeneration")] 
void RayGen() 
{
    HitInfo payload;
    payload.colorAndDistance = float4(0, 0, 0, 0);

    uint2 launchIndex = DispatchRaysIndex().xy;
    float2 dims = float2(DispatchRaysDimensions().xy);

    float2 ndc = float2(
        float(launchIndex.x + 0.5f) / dims.x * 2 - 1,
        float(dims.y - launchIndex.y - 1 + 0.5) / dims.y * 2 - 1);

    RayDesc ray;
    ray.Origin = gCamera.origin.xyz;
    ray.Direction = GetWorldRay(ndc, gCamera.forward.xyz, gCamera.right.xyz, gCamera.up.xyz);
    ray.TMin = 0;
    ray.TMax = 100000;

    TraceRay(
        SceneBVH,
        RAY_FLAG_NONE,
        0xFF, 
        0, // RayContributionToHitGroupIndex
        0, // MultiplierForGeometryContributionToHitGroupIndex
        0, // MissShaderIndex
        ray,
        payload);

    gOutput[launchIndex] = float4(payload.colorAndDistance.rgb, 1.f);
}


. TLAS, , SBT. TraceRay() payload . [shader("raygeneration")] , , , "RayGen" , .



closesthit



#include "Common.hlsl"
#include "Global.hlsl"

struct InstanceData
{
    float4 color;
};
ConstantBuffer<InstanceData> instanceData : register(b1);

[shader("closesthit")] 
void ClosestHit(inout HitInfo payload, Attributes attrib)
{
    float3 barycentrics = float3(1.f - attrib.bary.x - attrib.bary.y, attrib.bary.x, attrib.bary.y);

    float3 vertexNormals[3] = {
        Vertices[PrimitiveIndex() * 3].normal,
        Vertices[PrimitiveIndex() * 3 + 1].normal,
        Vertices[PrimitiveIndex() * 3 + 2].normal
    };

    float3 N = vertexNormals[0] * barycentrics.x +
        vertexNormals[1] * barycentrics.y +
        vertexNormals[2] * barycentrics.z;

    const float3 L = normalize(float3(0, -0.4, 1));

    payload.colorAndDistance = float4(instanceData.color.rgb * max(0, dot(N, L)), RayTCurrent());
}


. - payload. . . , .



miss.



#include "Common.hlsl"

[shader("miss")] 
void Miss(inout HitInfo payload : SV_RayPayload)
{
    payload.colorAndDistance = float4(0.0f, 0.2f, 0.7f, -1.0f);
}


— .



Main loop



main loop :



//...
//   root signature
dxrCommandList->SetComputeRootSignature(raytracingGlobalRootSignature.Get());

//   descriptor heap
dxrCommandList->SetDescriptorHeaps(1, descriptorHeap.GetAddressOf());

//   
dxrCommandList->SetComputeRootDescriptorTable(0, raytracingOutputResourceUAVGpuDescriptor);
dxrCommandList->SetComputeRootDescriptorTable(1, buffersGpuDescriptor);
dxrCommandList->SetComputeRootShaderResourceView(2, topLevelAccelerationStructure->GetGPUVirtualAddress());

//   
//...

D3D12_DISPATCH_RAYS_DESC dispatchDesc = {};
dispatchDesc.HitGroupTable.StartAddress = hitGroupShaderTable->GetGPUVirtualAddress();
dispatchDesc.HitGroupTable.SizeInBytes = hitGroupShaderTable->GetDesc().Width;
dispatchDesc.HitGroupTable.StrideInBytes = 64; //  hit shader record 
dispatchDesc.MissShaderTable.StartAddress = missShaderTable->GetGPUVirtualAddress();
dispatchDesc.MissShaderTable.SizeInBytes = missShaderTable->GetDesc().Width;
dispatchDesc.MissShaderTable.StrideInBytes = dispatchDesc.MissShaderTable.SizeInBytes;
dispatchDesc.RayGenerationShaderRecord.StartAddress = rayGenShaderTable->GetGPUVirtualAddress();
dispatchDesc.RayGenerationShaderRecord.SizeInBytes = rayGenShaderTable->GetDesc().Width;
dispatchDesc.Width = width;
dispatchDesc.Height = height;
dispatchDesc.Depth = 1;

dxrCommandList->SetPipelineState1(dxrStateObject.Get());

dxrCommandList->DispatchRays(&dispatchDesc);
//...






En este artículo, examinamos los objetos básicos necesarios para comenzar con DXR y también aplicamos este conocimiento en la práctica. Se omitieron deliberadamente las cuestiones de inicialización de DirectX, carga de modelos, compilación de sombreadores, etc., que no son relevantes. En los próximos artículos, se planea cambiar el énfasis, de hecho, en los gráficos, en lugar de trabajar con la API.



Enlaces:



https://github.com/k-payl/X12Lib/blob/master/src/examples/raytracing.cpp

https://microsoft.github.io/DirectX-Specs/d3d/Raytracing.html

https: // desarrollador. nvidia.com/rtx/raytracing/dxr/DX12-Raytracing-tutorial-Part-1




All Articles