아래 교재는 당신이 올린 블로그 글만을 바탕으로, Pipeline(레스터라이저·샘플러·블렌드 + 파이프라인 래퍼) 파트를 step-by-step로 재구성한 것입니다. 코드와 설명은 블로그 흐름을 벗어나지 않게 유지했고, 핵심 라인은 한 줄씩 짚어드립니다.


0) 목표와 전제

목표

  • 렌더링 단계(IA→VS→RS→PS→OM)에 흩어진 세팅 코드를 클래스 단위로 래핑하고,
  • 공통 세팅은 Pipeline으로, 개별 오브젝트 세팅은 세부 세터로 모듈화하여 조립형 파이프라인을 만든다.

전제

  • Graphics, VertexBuffer, IndexBuffer, InputLayout, VertexShader, PixelShader, ConstantBuffer<T>, Texture 준비 완료
  • 스마트 포인터 사용: shared_ptr
  • 공용 헤더(pch.h)에 Direct3D/DirectXTex, CHECK 매크로 포함

1) 상태 3총사: Rasterizer / Sampler / Blend

1-A) RasterizerState

헤더: RasterizerState.h

#pragma once
class RasterizerState
{
public:
    RasterizerState(ComPtr<ID3D11Device> device);
    ~RasterizerState();

    ComPtr<ID3D11RasterizerState> GetComPtr() { return _rasterizerState; }
    void Create();

private:
    ComPtr<ID3D11Device> _device;
    ComPtr<ID3D11RasterizerState> _rasterizerState;
};

구현: RasterizerState.cpp

#include "pch.h"
#include "RasterizerState.h"

RasterizerState::RasterizerState(ComPtr<ID3D11Device> device)
    : _device(device) {}

RasterizerState::~RasterizerState() {}

void RasterizerState::Create()
{
    D3D11_RASTERIZER_DESC desc;               // ▶ RS 상태 설명자
    ZeroMemory(&desc, sizeof(desc));          // ▶ 0 초기화
    desc.FillMode = D3D11_FILL_SOLID;         // ▶ 면 채우기(기본)
    desc.CullMode = D3D11_CULL_BACK;          // ▶ 뒷면 컬링
    desc.FrontCounterClockwise = false;       // ▶ 시계방향을 전면으로 간주

    HRESULT hr = _device->CreateRasterizerState(&desc, _rasterizerState.GetAddressOf()); // ▶ RS 생성
    CHECK(hr);
}

1-B) SamplerState

헤더: SamplerState.h

#pragma once
class SamplerState
{
public:
    SamplerState(ComPtr<ID3D11Device> device);
    ~SamplerState();

    ComPtr<ID3D11SamplerState> GetComPtr() { return _samplerState; }
    void Create();

private:
    ComPtr<ID3D11Device> _device;
    ComPtr<ID3D11SamplerState> _samplerState;
};

구현: SamplerState.cpp

#include "pch.h"
#include "SamplerState.h"

SamplerState::SamplerState(ComPtr<ID3D11Device> device)
    : _device(device) {}

SamplerState::~SamplerState() {}

void SamplerState::Create()
{
    D3D11_SAMPLER_DESC desc;                  // ▶ 샘플러 설명자
    ZeroMemory(&desc, sizeof(desc));
    desc.AddressU = D3D11_TEXTURE_ADDRESS_BORDER; // ▶ 경계 처리(U/V/W)
    desc.AddressV = D3D11_TEXTURE_ADDRESS_BORDER;
    desc.AddressW = D3D11_TEXTURE_ADDRESS_BORDER;
    desc.BorderColor[0] = 1; desc.BorderColor[1] = 0; // ▶ 경계색 (1,0,0,1)
    desc.BorderColor[2] = 0; desc.BorderColor[3] = 1;
    desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;    // ▶ 선형 필터
    desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;    // ▶ 비교 미사용
    desc.MaxLOD = FLT_MAX; desc.MinLOD = FLT_MIN;     // ▶ LOD 범위

    HRESULT hr = _device->CreateSamplerState(&desc, _samplerState.GetAddressOf()); // ▶ 샘플러 생성
    CHECK(hr);
}

1-C) BlendState

헤더: BlendState.h

#pragma once
class BlendState
{
public:
    BlendState(ComPtr<ID3D11Device> device);
    ~BlendState();

    const float* GetBlendFactor() { return &_blendFactor; }
    uint32 GetSampleMask() { return _sampleMask; }
    ComPtr<ID3D11BlendState> GetComPtr() { return _blendState; }

    void Create(D3D11_RENDER_TARGET_BLEND_DESC blendDesc =
        {
            true,                          // BlendEnable
            D3D11_BLEND_SRC_ALPHA,         // SrcBlend
            D3D11_BLEND_INV_SRC_ALPHA,     // DestBlend
            D3D11_BLEND_OP_ADD,            // BlendOp
            D3D11_BLEND_ONE,               // SrcBlendAlpha
            D3D11_BLEND_ZERO,              // DestBlendAlpha
            D3D11_BLEND_OP_ADD,            // BlendOpAlpha
            D3D11_COLOR_WRITE_ENABLE_ALL   // RenderTargetWriteMask
        }, float factor = 0.f);

private:
    ComPtr<ID3D11Device> _device;
    ComPtr<ID3D11BlendState> _blendState;
    float _blendFactor = 0.f;
    uint32 _sampleMask = 0xFFFFFFFF;
};

구현: BlendState.cpp

#include "pch.h"
#include "BlendState.h"

BlendState::BlendState(ComPtr<ID3D11Device> device)
    : _device(device) {}

BlendState::~BlendState() {}

void BlendState::Create(D3D11_RENDER_TARGET_BLEND_DESC blendDesc, float factor)
{
    _blendFactor = factor;                     // ▶ OMSetBlendState의 인자

    D3D11_BLEND_DESC desc;                     // ▶ 블렌드 설명자
    ZeroMemory(&desc, sizeof(D3D11_BLEND_DESC));
    desc.AlphaToCoverageEnable = false;
    desc.IndependentBlendEnable = false;
    desc.RenderTarget[0] = blendDesc;          // ▶ 0번 RT에만 적용

    HRESULT hr = _device->CreateBlendState(&desc, _blendState.GetAddressOf()); // ▶ 블렌드 상태 생성
    CHECK(hr);
}

적용(공용 include): pch.h
#include "RasterizerState.h"
#include "SamplerState.h"
#include "BlendState.h"

Game 멤버 교체
shared_ptr<RasterizerState> _rasterizerState;
shared_ptr<SamplerState> _samplerState;
shared_ptr<BlendState> _blendState;

초기화

_rasterizerState = make_shared<RasterizerState>(_graphics->GetDevice());
_samplerState    = make_shared<SamplerState>(_graphics->GetDevice());
_blendState      = make_shared<BlendState>(_graphics->GetDevice());
_rasterizerState->Create();
_samplerState->Create();
_blendState->Create();

2) ShaderScope 플래그 (재확인)

Shader.h에 이미 정의:

enum ShaderScope
{
    SS_None         = 0,
    SS_VertexShader = (1 << 0),
    SS_PixelShader  = (1 << 1),
    SS_Both         = SS_VertexShader | SS_PixelShader
};
  • 목적: 상수버퍼/텍스처/샘플러VS/PS 어느 쪽에 바인딩할지를 비트 플래그로 지정.

3) Pipeline 설계

3-A) 공용 정보 묶음: PipelineInfo

struct PipelineInfo
{
    shared_ptr<InputLayout>   inputLayout;       // IA
    shared_ptr<VertexShader>  vertexShader;      // VS
    shared_ptr<PixelShader>   pixelShader;       // PS
    shared_ptr<RasterizerState> rasterizerState; // RS
    shared_ptr<BlendState>      blendState;      // OM
    D3D11_PRIMITIVE_TOPOLOGY    topology = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
};
  • 의도: “포탄 갈아끼우듯” 공통 세팅을 한 번에 교체.

3-B) 클래스 골격: Pipeline.h

#pragma once

class Pipeline
{
public:
    Pipeline(ComPtr<ID3D11DeviceContext> deviceContext);
    ~Pipeline();

    void UpdatePipeline(PipelineInfo info); // ▶ 공통 세팅 일괄 반영

    void SetVertexBuffer(shared_ptr<VertexBuffer> buffer); // ▶ IA
    void SetIndexBuffer(shared_ptr<IndexBuffer> buffer);   // ▶ IA

    template<typename T>
    void SetConstantBuffer(uint32 slot, uint32 scope, shared_ptr<ConstantBuffer<T>> buffer)
    {
        if (scope & SS_VertexShader)
            _deviceContext->VSSetConstantBuffers(slot, 1, buffer->GetComPtr().GetAddressOf());
        if (scope & SS_PixelShader)
            _deviceContext->PSSetConstantBuffers(slot, 1, buffer->GetComPtr().GetAddressOf());
    }

    void SetTexture(uint32 slot, uint32 scope, shared_ptr<Texture> texture);               // ▶ SRV
    void SetSamplerState(uint32 slot, uint32 scope, shared_ptr<SamplerState> samplerState); // ▶ Sampler

    void Draw(uint32 vertexCount, uint32 startVertexLocation);                              // ▶ 정점만
    void DrawIndexed(uint32 indexCount, uint32 startIndexLocation, uint32 baseVertexLocation); // ▶ 인덱스 사용

private:
    ComPtr<ID3D11DeviceContext> _deviceContext;
};

3-C) 구현: Pipeline.cpp

#include "pch.h"
#include "Pipeline.h"

Pipeline::Pipeline(ComPtr<ID3D11DeviceContext> deviceContext)
    : _deviceContext(deviceContext) {}

Pipeline::~Pipeline() {}

void Pipeline::UpdatePipeline(PipelineInfo info)
{
    // === IA (공통) ===
    _deviceContext->IASetInputLayout(info.inputLayout->GetComPtr().Get());   // ▶ InputLayout
    _deviceContext->IASetPrimitiveTopology(info.topology);                   // ▶ Topology

    // === VS (공통) ===
    if (info.vertexShader)
        _deviceContext->VSSetShader(info.vertexShader->GetComPtr().Get(), nullptr, 0); // ▶ VS 바인딩

    // === RS (공통) ===
    if (info.rasterizerState)
        _deviceContext->RSSetState(info.rasterizerState->GetComPtr().Get());          // ▶ RS 바인딩

    // === PS (공통) ===
    if (info.pixelShader)
        _deviceContext->PSSetShader(info.pixelShader->GetComPtr().Get(), nullptr, 0); // ▶ PS 바인딩

    // === OM (공통) ===
    if (info.blendState)
        _deviceContext->OMSetBlendState(info.blendState->GetComPtr().Get(),
                                        info.blendState->GetBlendFactor(),
                                        info.blendState->GetSampleMask());            // ▶ OM 바인딩
}

// -------- IA 세부 세팅 --------
void Pipeline::SetVertexBuffer(shared_ptr<VertexBuffer> buffer)
{
    uint32 stride = buffer->GetStride();                     // ▶ 정점 1개 크기
    uint32 offset = buffer->GetOffset();                     // ▶ 시작 오프셋(일반 0)
    _deviceContext->IASetVertexBuffers(0, 1,
        buffer->GetComPtr().GetAddressOf(), &stride, &offset); // ▶ 슬롯0에 VB 바인딩
}

void Pipeline::SetIndexBuffer(shared_ptr<IndexBuffer> buffer)
{
    _deviceContext->IASetIndexBuffer(buffer->GetComPtr().Get(),
                                     DXGI_FORMAT_R32_UINT, 0);  // ▶ IB 바인딩
}

// -------- 자원(범위 지정) 세팅 --------
void Pipeline::SetTexture(uint32 slot, uint32 scope, shared_ptr<Texture> texture)
{
    if (scope & SS_VertexShader)
        _deviceContext->VSSetShaderResources(slot, 1, texture->GetComPtr().GetAddressOf());
    if (scope & SS_PixelShader)
        _deviceContext->PSSetShaderResources(slot, 1, texture->GetComPtr().GetAddressOf());
}

void Pipeline::SetSamplerState(uint32 slot, uint32 scope, shared_ptr<SamplerState> samplerState)
{
    if (scope & SS_VertexShader)
        _deviceContext->VSSetSamplers(slot, 1, samplerState->GetComPtr().GetAddressOf());
    if (scope & SS_PixelShader)
        _deviceContext->PSSetSamplers(slot, 1, samplerState->GetComPtr().GetAddressOf());
}

// -------- 드로우 명령 --------
void Pipeline::Draw(uint32 vertexCount, uint32 startVertexLocation)
{
    _deviceContext->Draw(vertexCount, startVertexLocation);     // ▶ 비인덱스 드로우
}

void Pipeline::DrawIndexed(uint32 indexCount, uint32 startIndexLocation, uint32 baseVertexLocation)
{
    _deviceContext->DrawIndexed(indexCount, startIndexLocation, baseVertexLocation); // ▶ 인덱스 드로우
}

핵심 포인트

  • UpdatePipeline()공통(자주 고정되는) 세팅만 처리
  • 오브젝트마다 바뀌는 것들(VB/IB/CB/SRV/Sampler)은 별도 Set함수로 제공
  • 상수버퍼/텍스처/샘플러는 scopeVS/PS 바인딩 위치를 분기

4) Game에 Pipeline 적용

4-A) 준비

  • pch.h#include "Pipeline.h"
  • Game 멤버:
shared_ptr<Pipeline> _pipeline;
  • 초기화:
_pipeline = make_shared<Pipeline>(_graphics->GetDeviceContext());

4-B) Render에서 조립

void Game::Render()
{
    _graphics->RenderBegin();

    // 1) 공통 세팅 한 방에
    PipelineInfo info;
    info.inputLayout    = _inputLayout;
    info.vertexShader   = _vertexShader;
    info.pixelShader    = _pixelShader;
    info.rasterizerState= _rasterizerState;
    info.blendState     = _blendState;
    _pipeline->UpdatePipeline(info);

    // 2) 오브젝트별 세팅
    _pipeline->SetVertexBuffer(_vertexBuffer);
    _pipeline->SetIndexBuffer(_indexBuffer);
    _pipeline->SetConstantBuffer(0, SS_VertexShader, _constantBuffer);
    _pipeline->SetTexture(0, SS_PixelShader, _texture1);
    _pipeline->SetSamplerState(0, SS_PixelShader, _samplerState);

    // 3) 드로우
    _pipeline->DrawIndexed(_geometry->GetIndexCount(), 0, 0);

    _graphics->RenderEnd();
}

라인별 의도

  • UpdatePipeline(info): IA/VS/RS/PS/OM 공통 요소를 일괄 적용
  • SetVertexBuffer/SetIndexBuffer: IA 세부(각 메시별 다름)
  • SetConstantBuffer: slot=0VS에 바인딩(블로그 예)
  • SetTexture/SetSamplerState: slot=0PS에 바인딩(블로그 예)
  • DrawIndexed: Geometry 인덱스 수로 그리기

5) 체크리스트 & 트러블 슈팅

체크리스트

  • Rasterizer/Sampler/BlendStateshared_ptr로 교체하고 Create() 호출했는가
  • PipelineInfo빠뜨린 공통 요소가 없는가 (IL/VS/PS/RS/Blend/Topology)
  • VB/IB/CB/SRV/Sampler는 Pipeline의 Set* 함수로 넘겼는가
  • 상수버퍼/텍스처/샘플러의 scope가 의도대로(VS/PS) 설정됐는가
  • Draw vs DrawIndexed를 상황에 맞게 선택했는가

자주 겪는 오류

  • UpdatePipeline에서 InputLayout/Topology 누락 → 화면 미표시
  • SetIndexBuffer 포맷 불일치 → 그리기 실패
  • scope 비트 플래그 누락 → 리소스 바인딩 안 됨
  • 상태 객체 Create() 호출 누락 → OM/RS/PSSet*에서 무효 핸들

6) 실습 가이드

  1. 블렌드 교체 실습
D3D11_RENDER_TARGET_BLEND_DESC alphaDesc = {
    true, D3D11_BLEND_SRC_ALPHA, D3D11_BLEND_INV_SRC_ALPHA,
    D3D11_BLEND_OP_ADD, D3D11_BLEND_ONE, D3D11_BLEND_ZERO,
    D3D11_BLEND_OP_ADD, D3D11_COLOR_WRITE_ENABLE_ALL
};
_blendState->Create(alphaDesc);  // 투명 합성

UpdatePipeline(info)만으로 OM 상태가 바뀌는지 확인

  1. 샘플러/텍스처 범위 실습
_pipeline->SetTexture(0, SS_Both, _texture1);
_pipeline->SetSamplerState(0, SS_Both, _samplerState);

→ VS/PS 모두에서 텍스처 접근되는지 HLSL로 확인

  1. 인덱스 없는 드로우 실습
_pipeline->SetIndexBuffer(nullptr); // (실습용) 세팅 생략
_pipeline->Draw(_geometry->GetVertexCount(), 0);

→ 인덱스 없이도 정상 출력되는지 비교


7) 30초 구두 요약(면접용)

“우리는 렌더링 파이프라인의 공통 세팅(IL/VS/RS/PS/OM)을 PipelineInfo로 묶고, Pipeline::UpdatePipeline()에서 한 번에 적용합니다.
오브젝트마다 달라지는 VB/IB/CB/SRV/Sampler는 Pipeline의 세터로 분리해 조립합니다.
상수버퍼/텍스처/샘플러는 ShaderScope 비트 플래그로 VS/PS 바인딩 범위를 지정합니다.
이렇게 하면 Game::Render는 ‘정보 구성 → 공통 업데이트 → 세부 세팅 → Draw’의 짧은 패턴으로 정리됩니다.”


필요하시면 같은 스타일로 Material / MeshRenderer / RenderItem(=Mesh+Material+Transform) 묶음까지 확장하는 단계도 이어서 정리해드릴게요.

profile
李家네_공부방

0개의 댓글