[DirectX12][DXR] DXR프로그래밍-1(Raytracing Pipeline State Object)

윤태웅·2022년 8월 19일
0

DirectX12

목록 보기
3/11

개요

22년 7월~8월 까지 DXR프로그래밍을 열심히 했다.
깃헙 링크
코딩을 진행하면서 이해한 DXR개념들을 간단히 정리한다.

DXR이란

DXR은 2018년 10월 기존 DirectX12 API에 새로 추가된 기능이다.
DXR의 약자는 (DirectX Raytracing)이다. 이전까지 Real-Time에서 레이트레이싱을 지원하는 그래픽스 API는 없었는데, 새롭게 등장한 기능이라고 할 수 있다. 이 API도 그래도 나온지 꽤 시간이 되었다고 다양한 AAA게임에서 DXR을 지원하는 것을 종종 볼 수 있다. 사펑이라던가...
배틀필드라던가... 등등의 게임에서 DXR을 지원한다. 하지만, 최신 GPU를 사용해도 성능상의 제약이 크다. (ex/rtx 3080GPU가 4k 60fps방어를 하지 못한다). 하지만, 머지않아 성능좋은 GPU가 잘 보급되어 어지간한 게임들에서 레이트레이싱 옵션이 등장할지도 모르겠다.

DXR프로그래밍

이제 이 DXR을 직접 이용해보자..
Vertex Shader, Pixel Shader등등을 정의하는 기존의 Rasterization방식과 달리 전혀 새로운 개념들이 나온다. 일단 DXR을 이용하기 위한 기본인 Raytracing Pipeline State Object를 살펴보겠다

Raytracing State Object

DXIL Subobject

CD3DX12_DXIL_LIBRARY_SUBOBJECT* lib = m_stateObjectDesc.CreateSubobject<CD3DX12_DXIL_LIBRARY_SUBOBJECT>();
        D3D12_SHADER_BYTECODE libdxil = CD3DX12_SHADER_BYTECODE(pShaderByteCode, shaderByteCodeSize);
        lib->SetDXILLibrary(&libdxil);
        lib->DefineExport(entryPointName);

Raytracing State Object에는 Subobject라고 구성요소를 만들어줄 수가 있다.
가장 먼저 만들어주는 Subobject는 DXIL Subobject이다. 이 SubObject의 역할은
Shader코드의 바이트코드를 읽어서 EntryPoint를 노출시켜주는 역할을 한다.
위 코드에서 DefineExport함수 콜의 의미는 '이 문자열로 DXIL Subobject가 읽은 Shader코드를 참조할 수 있다'는 의미이다.

Hit Group Subobject

for (UINT i = 0; i < RayType::Count; i++)
       {
           CD3DX12_HIT_GROUP_SUBOBJECT* hitGroup = m_stateObjectDesc.CreateSubobject<CD3DX12_HIT_GROUP_SUBOBJECT>();
           hitGroup->SetClosestHitShaderImport(CLOSEST_HIT_SHADER_NAMES[i]);//히트그룹과 연결될 셰이더진입점
           hitGroup->SetHitGroupExport(HIT_GROUP_NAMES[i]);                 //히트 그룹 수출
           hitGroup->SetHitGroupType(D3D12_HIT_GROUP_TYPE_TRIANGLES);       //이 히트그룹은 삼각형
       }

두번째로 만들어주는 Subobject는 Hit Group SubObject이다.
DXR에서는 Ray와 Object충돌 했을때 어떤 처리를 하는 Shader들이 존재한다(Anyhit,Closest Hit...). 이 충돌처리 Shader를 하나의 단위로 묶어준다. 이렇게 묶어주는 이유는 DXR API가 Ray 충돌테스트를 할때 실행해야하는 Hit Group Shader들을 Object마다 알고있어야 하기 때문이다.

Shader Config Subobject

CD3DX12_RAYTRACING_SHADER_CONFIG_SUBOBJECT* shaderConfig = m_stateObjectDesc.CreateSubobject<CD3DX12_RAYTRACING_SHADER_CONFIG_SUBOBJECT>();
       UINT payloadSize = max(sizeof(RayPayload), sizeof(ShadowRayPayload));   //  둘중 큰값을 할당
       UINT attributeSize = sizeof(XMFLOAT2);                                  //  barycentrics
       shaderConfig->Config(payloadSize, attributeSize);                       //  payload, attribute사이즈 정의(셰이더에서 인자로 사용됨)

세번째로 만들어주는 Subobject는 Shader Config Subobject이다.
Payload사이즈와 AttributeSize를 정의한다.
이때 Payload는 TraceRay를 호출하면서 Shader사이에 전달되는 값들의 집합(Color,recursionDepth)이고 AttributeSize는 DXR이 Ray로 충돌처리를 하면 Polygon의 무게중심좌표계(Barycentric)좌표를 부여해주는데, 이 좌표계에 저장될 데이터 사이즈를 의미한다.

Global Root Signature Subobject

CD3DX12_GLOBAL_ROOT_SIGNATURE_SUBOBJECT* globalRootSignature = m_stateObjectDesc.CreateSubobject<CD3DX12_GLOBAL_ROOT_SIGNATURE_SUBOBJECT>();// 글로벌 루트시그니처 서브오브젝트생성
        globalRootSignature->SetRootSignature(pGlobalRootSignature.Get());

네번째로 만들어주는 Subobject는 Global Root Signature Subobject이다.
Global의 의미는 모든 Shader에서 공통으로 참조할 수 있는 Signature라는 의미이다.
보통 Global Root Signature로 참조되는 Shader변수는 앞에 'g_'접두사를 붙힌다거나 해서 구별한다.

Local Root Signature Subobject

CD3DX12_LOCAL_ROOT_SIGNATURE_SUBOBJECT* localRootSignature = m_stateObjectDesc.CreateSubobject<CD3DX12_LOCAL_ROOT_SIGNATURE_SUBOBJECT>();
        localRootSignature->SetRootSignature(pLocalRootSignature.Get());
        CD3DX12_SUBOBJECT_TO_EXPORTS_ASSOCIATION_SUBOBJECT* rootSignatureAssociation = m_stateObjectDesc.CreateSubobject<CD3DX12_SUBOBJECT_TO_EXPORTS_ASSOCIATION_SUBOBJECT>();
        rootSignatureAssociation->SetSubobjectToAssociate(*localRootSignature);
        rootSignatureAssociation->AddExport(HIT_GROUP_NAMES[RayType::Radiance]);//Radiance Hit Group에서 사용하겠다

다섯번째로 만들어주는 Subobject는 Local Root Signature Subobject이다.
필자가 생각하기에 레이트레이싱 API에서 가장 이해하기 어려운 개념이다.
아직 언급하진 않았지만 DXR실행을 위해 Shader Table을 생성하게 되는데 Shader Table의
각각의 Shader Record에 저장될 데이터들의 명세를 선언하는것이 Local Root Signature이다.
Global Root Signature와의 차이점은 Local Root Signature에 의해 참조되는 Shader변수는
충돌한 Geometry의 Hit Group Shader Record마다 담겨있는 데이터가 달라진다는 것이라고 할 수 있다. Global Root Signature같이 Local Root Signature또한 참조되는 Shader변수 앞에 'l_'접두사를 붙힌다거나 해서 Global변수와 구별한다.

Pipeline Config Subobject

CD3DX12_RAYTRACING_PIPELINE_CONFIG_SUBOBJECT* pipelineConfig = m_stateObjectDesc.CreateSubobject<CD3DX12_RAYTRACING_PIPELINE_CONFIG_SUBOBJECT>();// 파이프라인 config 서브오브젝트 생성 
        UINT maxRecursionDepth = MAX_RECURSION_DEPTH;
        pipelineConfig->Config(maxRecursionDepth);//적용

마지막으로 만들어주는 Subobject는 Pipeline Config Subobject이다.
이 Subobject에는 DXR이 TraceRay함수 호출을 하는 최대 재귀호출 Depth를 선언해준다.
DXR에서는 최대 20으로 제한을 두고 있다. 이렇게 제한을 두는 이유는
연산 부하 때문이다.

0개의 댓글