Directx 12의 레이트레이싱 하드웨어 쿼리를 API에 도입시켜 Path Tracing을 위한 특정 씬의 광원계산, 반사, 전역조명, 텍스처에 라이트 맵 또는 물리 연산 지오메트리 베이킹, 공간 후 처리 효과 등등 다양한 사례를 쉽고 빠르게 수행 할 수 있습니다.
다이렉트x 12의 레이트레이싱 쉐이더 스테이지는 총 5종류 입니다.
// 🌎 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);
}
레이트레이싱 파이프라인은 쉐이더가 읽을 수 있는 리소스를 설명하기 위해 로컬과 글로벌 루트 시그니처가 모두 필요합니다.
글로벌 루트 시그니처는 가속 구조체나 레디언스 버퍼와 같이 서로 다른 dispatchRays
호출 간에 공유하여 읽고 쓸 수 있는 데이터에 사용될 수 있습니다.
// 👋 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;
}
로컬 루트 시그니처는 레이트레이싱의 각 프로그래밍 가능한 단계가 자체적으로 정의할 수 있다는 점에서 특별합니다.