🧱 Root Signature

Root Signature는 셰이더가 사용할 리소스들의 '기안서'라고 볼 수 있습니다. 즉, GPU 내에서 어떤 리소스를 사용할 것인지 명시하는 구조입니다.

D3D12_ROOT_SIGNATURE_DESC sigDesc = CD3DX12_ROOT_SIGNATURE_DESC(D3D12_DEFAULT);
sigDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
  • ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT 플래그는 정점 레이아웃 정보를 사용하도록 허용합니다.
  • D3D12SerializeRootSignature로 직렬화하고, CreateRootSignature로 생성합니다.

Root Signature는 기능이 확장될수록 더 많은 파라미터가 필요하므로, 기초 렌더링에서는 간단한 형태로 시작합니다.


🧩 Mesh 클래스

Mesh는 정점 데이터(Vertex)를 관리하며, GPU에 업로드할 VertexBuffer를 생성합니다.

D3D12_HEAP_PROPERTIES heapProperty = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD);
D3D12_RESOURCE_DESC desc = CD3DX12_RESOURCE_DESC::Buffer(bufferSize);
  • UPLOAD 메모리: CPU가 접근 가능하며 실시간 변경에 적합 (ex. UI, 움직이는 객체)
  • DEFAULT 메모리: GPU 전용, 읽기 전용 데이터 (ex. 나무, 돌처럼 고정된 오브젝트)

버퍼에 데이터를 복사하는 과정은 다음과 같습니다:

_vertexBuffer->Map(0, &readRange, &vertexDataBuffer);
::memcpy(vertexDataBuffer, &vec[0], bufferSize);
_vertexBuffer->Unmap(0, nullptr);

🧾 Mesh 렌더링

CMD_LIST->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
CMD_LIST->IASetVertexBuffers(0, 1, &_vertexBufferView);
CMD_LIST->DrawInstanced(_vertexCount, 1, 0, 0);
  • TriangleList: 3개의 정점을 하나의 삼각형으로 인식
  • 실제로 GPU는 이 명령들을 Command List에 쌓아뒀다가, Render 단계에서 실행합니다.

🧠 Shader 구조와 작성

셰이더는 HLSL(HIGH LEVEL SHADER LANGUAGE)로 작성합니다. 보통 .hlsl 또는 .hlsli 확장자로 저장합니다.

struct VS_IN {
    float3 pos : POSITION;
    float4 color : COLOR;
};

struct VS_OUT {
    float4 pos : SV_Position;
    float4 color : COLOR;
};

VS_OUT VS_Main(VS_IN input) {
    VS_OUT output;
    output.pos = float4(input.pos, 1.f);
    output.color = input.color;
    return output;
}

float4 PS_Main(VS_OUT input) : SV_Target {
    return input.color;
}
  • Vertex Shader: 정점 위치와 색상을 전달
  • Pixel Shader: 픽셀마다 색상 출력

이 셰이더를 컴파일하여 GPU가 사용할 수 있도록 합니다.

D3DCompileFromFile(path.c_str(), nullptr, ..., "VS_Main", "vs_5_0", ..., &blob, ...);

컴파일 후 얻은 Blob은 PSO 설정 시 셰이더 코드로 삽입됩니다.


🧮 Input Layout

정점 데이터의 레이아웃을 설명해주는 구조입니다. 이는 Vertex Shader의 입력과 정확히 일치해야 합니다.

D3D12_INPUT_ELEMENT_DESC layout[] = {
    { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, ..., 0 },
    { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, ..., 12 },
};

🎨 삼각형 데이터 정의

임시로 삼각형 하나를 생성해 화면 출력 테스트를 합니다.

vector<Vertex> vec(3);
vec[0].pos = Vec3(0.f, 0.5f, 0.5f); vec[0].color = Vec4(1.f, 0.f, 0.f, 1.f);
vec[1].pos = Vec3(0.5f, -0.5f, 0.5f); vec[1].color = Vec4(0.f, 1.0f, 0.f, 1.f);
vec[2].pos = Vec3(-0.5f, -0.5f, 0.5f); vec[2].color = Vec4(0.f, 0.f, 1.f, 1.f);

정점은 위치(Pos)와 색상(Color) 정보를 갖고 있으며, 화면 중심이 (0,0)입니다.


🎯 Draw Call

CMD_LIST->DrawInstanced(3, 1, 0, 0);
  • 정점 3개로 삼각형 하나를 그립니다.
  • 다양한 Draw 함수들이 존재하며, 목적에 따라 DrawIndexed, DrawInstanced 등을 사용할 수 있습니다.

✅ 전체 렌더링 흐름 요약

순서단계설명
1Device & SwapChain 생성장치 초기화
2VertexBuffer 생성정점 데이터 업로드
3RootSignature 설정셰이더 자원 정의
4Shader 컴파일 및 PSO 설정렌더링 파이프라인 구성
5Viewport/Scissor 설정화면 설정
6CommandList에 렌더 명령 입력GPU에 명령 전달
7Present출력 버퍼 교체

🧵 Tip: 렌더링 파이프라인 구성도 그려보기

이 단계들을 이해했다면, 렌더링 파이프라인을 도식으로 그려보며 흐름을 시각화하는 것도 추천합니다.
(예: Input Assembler → Vertex Shader → Rasterizer → Pixel Shader → Output Merger)


profile
李家네_공부방

0개의 댓글