아래 교재는 당신이 올린 블로그 글만을 바탕으로, Pipeline(레스터라이저·샘플러·블렌드 + 파이프라인 래퍼) 파트를 step-by-step로 재구성한 것입니다. 코드와 설명은 블로그 흐름을 벗어나지 않게 유지했고, 핵심 라인은 한 줄씩 짚어드립니다.
목표
Pipeline으로, 개별 오브젝트 세팅은 세부 세터로 모듈화하여 조립형 파이프라인을 만든다.전제
Graphics, VertexBuffer, IndexBuffer, InputLayout, VertexShader, PixelShader, ConstantBuffer<T>, Texture 준비 완료shared_ptrRasterizerState.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);
}
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);
}
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();
Shader.h에 이미 정의:
enum ShaderScope
{
SS_None = 0,
SS_VertexShader = (1 << 0),
SS_PixelShader = (1 << 1),
SS_Both = SS_VertexShader | SS_PixelShader
};
PipelineInfostruct 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;
};
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;
};
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함수로 제공
- 상수버퍼/텍스처/샘플러는
scope로 VS/PS 바인딩 위치를 분기
pch.h에 #include "Pipeline.h"Game 멤버:shared_ptr<Pipeline> _pipeline;
_pipeline = make_shared<Pipeline>(_graphics->GetDeviceContext());
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=0을 VS에 바인딩(블로그 예)SetTexture/SetSamplerState: slot=0을 PS에 바인딩(블로그 예)DrawIndexed: Geometry 인덱스 수로 그리기체크리스트
Rasterizer/Sampler/BlendState를 shared_ptr로 교체하고 Create() 호출했는가PipelineInfo에 빠뜨린 공통 요소가 없는가 (IL/VS/PS/RS/Blend/Topology)scope가 의도대로(VS/PS) 설정됐는가Draw vs DrawIndexed를 상황에 맞게 선택했는가자주 겪는 오류
UpdatePipeline에서 InputLayout/Topology 누락 → 화면 미표시SetIndexBuffer 포맷 불일치 → 그리기 실패scope 비트 플래그 누락 → 리소스 바인딩 안 됨Create() 호출 누락 → OM/RS/PSSet*에서 무효 핸들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 상태가 바뀌는지 확인
_pipeline->SetTexture(0, SS_Both, _texture1);
_pipeline->SetSamplerState(0, SS_Both, _samplerState);
→ VS/PS 모두에서 텍스처 접근되는지 HLSL로 확인
_pipeline->SetIndexBuffer(nullptr); // (실습용) 세팅 생략
_pipeline->Draw(_geometry->GetVertexCount(), 0);
→ 인덱스 없이도 정상 출력되는지 비교
“우리는 렌더링 파이프라인의 공통 세팅(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) 묶음까지 확장하는 단계도 이어서 정리해드릴게요.