Directx 12의 레이트레이싱 하드웨어 쿼리를 API에 도입시켜 Path Tracing을 위한 특정 씬의 광원계산, 반사, 전역조명, 텍스처에 라이트 맵 또는 물리 연산 지오메트리 베이킹, 공간 후 처리 효과 등등 다양한 사례를 쉽고 빠르게 수행 할 수 있습니다.

Compiling Shaders

image.png

다이렉트x 12의 레이트레이싱 쉐이더 스테이지는 총 5종류 입니다.

image.png

// 🌎 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);
}

Root Signature

image.png

레이트레이싱 파이프라인은 쉐이더가 읽을 수 있는 리소스를 설명하기 위해 로컬글로벌 루트 시그니처가 모두 필요합니다.

글로벌 루트 시그니처는 가속 구조체나 레디언스 버퍼와 같이 서로 다른 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;
}

로컬 루트 시그니처는 레이트레이싱의 각 프로그래밍 가능한 단계가 자체적으로 정의할 수 있다는 점에서 특별합니다.