Company
교육 철학

레스터라이저 Rasterizer, 뎁스 스텐실 DepthStencil, 블렌드 Blend (State)

개요

DirectX 11 렌더링 파이프라인에서 Rasterizer State, Depth/Stencil State, Blend State는 픽셀이 최종적으로 화면에 그려지는 방식을 제어하는 핵심 상태 객체들입니다. 이 세 가지 State는 Output Merger 단계에서 함께 작동하며, 렌더링의 품질과 성능을 결정합니다.

렌더링 파이프라인에서의 위치

이 문서에서는 각 State의 역할, 설정 방법, 실전 활용 패턴을 다룹니다.

1. Rasterizer Stage (래스터라이저 단계)

Rasterization이란?

버텍스 셰이더와 지오메트리 셰이더로부터 출력된 정점 정보(벡터)를 화면의 픽셀(래스터)로 변환하는 과정입니다. 이 단계는 고정 파이프라인(Fixed Pipeline)으로 프로그래머가 셰이더 코드를 작성할 수 없으며, GPU가 내장된 알고리즘으로 자동 처리합니다.

Rasterization의 주요 작업

1. 클리핑 (Clipping)
뷰 프러스텀(View Frustum) 밖의 지오메트리 제거
Near/Far Plane 밖의 정점 잘라내기
부분적으로 보이는 삼각형은 경계에서 자르고 새로운 정점 생성
2. 원근 나눗셈 (Perspective Division)
동차 좌표계(Homogeneous Coordinates)에서 정규화 장치 좌표계(NDC)로 변환
(x, y, z, w)(x/w, y/w, z/w)
원근 투영(Perspective Projection)의 핵심 단계
3. 뒷면 제거 (Backface Culling)
카메라를 향하지 않는 면(뒷면) 제거
성능 최적화: 약 50% 렌더링 부하 감소
정점 순서(Winding Order)로 앞면/뒷면 판단
4. 스캔 변환 (Scan Conversion)
삼각형 내부의 픽셀 위치 계산
각 픽셀에 대한 보간(Interpolation) 값 생성
정점 속성(색상, 텍스처 좌표, 노말 등)을 픽셀별로 보간
5. 뷰포트 변환 (Viewport Transform)
NDC 좌표(-1 ~ 1)를 화면 픽셀 좌표로 변환
뷰포트 크기와 위치 적용
깊이 값도 0 ~ 1 범위로 변환

Rasterizer State 생성

// Rasterizer State 설정 구조체 D3D11_RASTERIZER_DESC rsDesc = {}; rsDesc.FillMode = D3D11_FILL_SOLID; // 면 채우기 모드 rsDesc.CullMode = D3D11_CULL_BACK; // 뒷면 컬링 rsDesc.FrontCounterClockwise = FALSE; // 시계방향이 앞면 rsDesc.DepthBias = 0; // 깊이 바이어스 rsDesc.DepthBiasClamp = 0.0f; // 바이어스 최대값 rsDesc.SlopeScaledDepthBias = 0.0f; // 경사 기반 바이어스 rsDesc.DepthClipEnable = TRUE; // 깊이 클리핑 활성화 rsDesc.ScissorEnable = FALSE; // 시저 테스트 비활성화 rsDesc.MultisampleEnable = FALSE; // MSAA 비활성화 rsDesc.AntialiasedLineEnable = FALSE; // 라인 AA 비활성화 // Rasterizer State 객체 생성 ID3D11RasterizerState* rasterizerState = nullptr; HRESULT hr = device->CreateRasterizerState(&rsDesc, &rasterizerState);
C++
복사

주요 속성 상세 설명

FillMode
enum D3D11_FILL_MODE { D3D11_FILL_WIREFRAME = 2, // 와이어프레임 (선만) D3D11_FILL_SOLID = 3 // 솔리드 (면 채우기) };
C++
복사
CullMode
enum D3D11_CULL_MODE { D3D11_CULL_NONE = 1, // 컬링 없음 (양면 렌더링) D3D11_CULL_FRONT = 2, // 앞면 컬링 D3D11_CULL_BACK = 3 // 뒷면 컬링 (일반적) };
C++
복사
CULL_BACK: 가장 일반적, 닫힌 메시에 적합
CULL_NONE: 나뭇잎, 천 등 양면 렌더링이 필요한 경우
CULL_FRONT: 내부 렌더링, 그림자 매핑의 Shadow Acne 해결
FrontCounterClockwise
FALSE: 시계 방향(Clockwise) 정점 배열이 앞면 (DirectX 기본)
TRUE: 반시계 방향(Counter-Clockwise)이 앞면 (OpenGL 스타일)
Depth Bias (깊이 편향)
용도: Z-Fighting 문제 해결
활용: 데칼, 그림자 매핑, 아웃라인 렌더링
rsDesc.DepthBias = 100; // 정수 오프셋 rsDesc.SlopeScaledDepthBias = 1.0f; // 경사에 비례한 오프셋
C++
복사

Rasterizer State 바인딩

void GraphicDevice_DX11::BindRasterizerState(ID3D11RasterizerState* rasterizerState) { context->RSSetState(rasterizerState); } // 사용 예시 BindRasterizerState(rasterizerStates[eRasterizerState::SolidBack].Get());
C++
복사

실전 Rasterizer State 세트

enum class eRasterizerState { SolidBack, // 기본 렌더링 SolidFront, // 내부 렌더링 SolidNone, // 양면 렌더링 Wireframe, // 디버그 와이어프레임 ShadowMap, // 그림자 맵 생성용 Count }; // 초기화 시 모든 State 생성 void InitializeRasterizerStates() { D3D11_RASTERIZER_DESC desc = {}; // 1. SolidBack: 일반 렌더링 desc.FillMode = D3D11_FILL_SOLID; desc.CullMode = D3D11_CULL_BACK; device->CreateRasterizerState(&desc, &states[SolidBack]); // 2. SolidFront: 내부 렌더링 desc.CullMode = D3D11_CULL_FRONT; device->CreateRasterizerState(&desc, &states[SolidFront]); // 3. SolidNone: 양면 렌더링 desc.CullMode = D3D11_CULL_NONE; device->CreateRasterizerState(&desc, &states[SolidNone]); // 4. Wireframe: 디버그 desc.FillMode = D3D11_FILL_WIREFRAME; device->CreateRasterizerState(&desc, &states[Wireframe]); // 5. ShadowMap: 그림자 매핑용 desc.FillMode = D3D11_FILL_SOLID; desc.CullMode = D3D11_CULL_FRONT; // 앞면 컬링으로 Shadow Acne 방지 desc.DepthBias = 100; desc.SlopeScaledDepthBias = 1.0f; device->CreateRasterizerState(&desc, &states[ShadowMap]); }
C++
복사

2. Output Merger Stage (출력 병합 단계)

Output Merger의 역할

Output Merger (OM) 단계는 렌더링 파이프라인의 최종 단계로, 픽셀 셰이더에서 출력된 픽셀 값을 렌더 타겟에 어떻게 병합할지 결정합니다.

주요 기능

렌더 타겟 관리
동시에 최대 8개의 렌더 타겟 사용 가능 (MRT: Multiple Render Targets)
1개의 Depth/Stencil 버퍼 사용
각 렌더 타겟은 독립적인 블렌딩 설정 가능
병합 작업 순서
1.
Depth/Stencil Test: 깊이 및 스텐실 테스트 수행
2.
Blending: 새 픽셀과 기존 픽셀 혼합
3.
Render Target Write: 최종 값을 렌더 타겟에 기록

3. Blend State (블렌드 상태)

블렌딩의 개념

블렌딩은 픽셀 셰이더 출력 값(Source)렌더 타겟의 기존 값(Destination)을 수학적으로 결합하여 최종 픽셀 색상을 결정하는 과정입니다.

블렌딩 수식

구성 요소
SourceColor: 픽셀 셰이더 출력 (새로 그릴 픽셀)
DestColor: 렌더 타겟의 기존 픽셀
SourceBlend: Source에 곱할 계수
DestBlend: Dest에 곱할 계수
BlendOp: 혼합 연산 (덧셈, 뺄셈 등)

일반적인 블렌딩 공식

알파 블렌딩 (투명도)
반투명 UI, 유리, 물 렌더링에 사용
알파 값으로 투명도 제어
가산 블렌딩 (Additive)
불꽃, 빛 효과, 파티클에 사용
밝기 증가 효과
곱셈 블렌딩 (Multiplicative)
어두운 효과, 그림자, 라이트맵에 사용
색상 필터링

Blend State 생성

// Blend State 설정 구조체 D3D11_BLEND_DESC blendDesc = {}; // 전역 설정 blendDesc.AlphaToCoverageEnable = false; // MSAA에서 알파 사용 여부 blendDesc.IndependentBlendEnable = false; // false면 RT[0]만 사용, RT[1-7] 무시 // 렌더 타겟 0번 설정 blendDesc.RenderTarget[0].BlendEnable = true; // 블렌딩 활성화 // RGB 블렌딩 설정 blendDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA; // Source 계수 blendDesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; // Dest 계수 blendDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; // 연산: 덧셈 // Alpha 블렌딩 설정 blendDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; // Alpha Source blendDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO; // Alpha Dest blendDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; // Alpha 연산 // 쓰기 마스크 (어떤 채널에 쓸지) blendDesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; // Blend State 객체 생성 ID3D11BlendState* blendState = nullptr; device->CreateBlendState(&blendDesc, &blendState);
C++
복사

주요 속성 상세 설명

AlphaToCoverageEnable
MSAA(멀티샘플 안티앨리어싱)와 함께 사용
알파 값에 따라 샘플 커버리지 결정
나뭇잎, 풀 등 알파 테스트 대체
IndependentBlendEnable
FALSE: RenderTarget[0]만 사용, 나머지는 동일 설정
TRUE: 각 렌더 타겟이 독립적인 블렌드 설정 가능
MRT 사용 시 TRUE로 설정
Blend Factor (계수)
enum D3D11_BLEND { D3D11_BLEND_ZERO, // 0 D3D11_BLEND_ONE, // 1 D3D11_BLEND_SRC_COLOR, // Source RGB D3D11_BLEND_INV_SRC_COLOR, // 1 - Source RGB D3D11_BLEND_SRC_ALPHA, // Source Alpha D3D11_BLEND_INV_SRC_ALPHA, // 1 - Source Alpha D3D11_BLEND_DEST_COLOR, // Dest RGB D3D11_BLEND_INV_DEST_COLOR, // 1 - Dest RGB D3D11_BLEND_DEST_ALPHA, // Dest Alpha D3D11_BLEND_INV_DEST_ALPHA, // 1 - Dest Alpha D3D11_BLEND_BLEND_FACTOR, // 커스텀 상수 // ... 더 많은 옵션 };
C++
복사
Blend Operation (연산)
enum D3D11_BLEND_OP { D3D11_BLEND_OP_ADD, // Src + Dest D3D11_BLEND_OP_SUBTRACT, // Src - Dest D3D11_BLEND_OP_REV_SUBTRACT, // Dest - Src D3D11_BLEND_OP_MIN, // min(Src, Dest) D3D11_BLEND_OP_MAX // max(Src, Dest) };
C++
복사
Color Write Mask
enum D3D11_COLOR_WRITE_ENABLE { D3D11_COLOR_WRITE_ENABLE_RED = 1, // R 채널만 D3D11_COLOR_WRITE_ENABLE_GREEN = 2, // G 채널만 D3D11_COLOR_WRITE_ENABLE_BLUE = 4, // B 채널만 D3D11_COLOR_WRITE_ENABLE_ALPHA = 8, // A 채널만 D3D11_COLOR_WRITE_ENABLE_ALL = 15 // RGBA 모두 };
C++
복사

기본 Blend State 값

만약 따로 설정하지 않는다면 다음과 같은 기본값으로 설정됩니다:
기본값 특징
BlendEnable = FALSE: 블렌딩 비활성화 (불투명 렌더링)
픽셀 셰이더 출력이 그대로 렌더 타겟에 기록
알파 값은 무시됨

Blend State 바인딩

// Blend Factor: D3D11_BLEND_BLEND_FACTOR에서 사용할 상수 FLOAT blendFactor[4] = {0.0f, 0.0f, 0.0f, 0.0f}; // RGBA // Sample Mask: 멀티샘플 렌더 타겟에서 어떤 샘플을 활성화할지 UINT sampleMask = 0xffffffff; // 모든 샘플 사용 // Output Merger에 바인딩 context->OMSetBlendState( blendState, // Blend State 객체 blendFactor, // Blend Factor 상수 sampleMask // Sample Mask );
C++
복사
BlendFactor 활용
// 커스텀 블렌드 팩터 사용 예시 blendDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_BLEND_FACTOR; blendDesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_BLEND_FACTOR; // 바인딩 시 팩터 값 지정 FLOAT customFactor[4] = {1.0f, 0.5f, 0.5f, 1.0f}; // 빨간색 강조 context->OMSetBlendState(blendState, customFactor, 0xffffffff);
C++
복사

실전 Blend State 패턴

enum class eBlendState { Default, // 불투명 (블렌딩 없음) AlphaBlend, // 알파 블렌딩 Additive, // 가산 블렌딩 Multiplicative, // 곱셈 블렌딩 NonPremultiplied,// 비사전곱셈 알파 Count }; void InitializeBlendStates() { D3D11_BLEND_DESC desc = {}; // 1. Default: 불투명 렌더링 desc.RenderTarget[0].BlendEnable = false; desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; device->CreateBlendState(&desc, &states[Default]); // 2. AlphaBlend: 표준 알파 블렌딩 desc.RenderTarget[0].BlendEnable = true; desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA; desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO; desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; device->CreateBlendState(&desc, &states[AlphaBlend]); // 3. Additive: 가산 블렌딩 (파티클 효과) desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA; desc.RenderTarget[0].DestBlend = D3D11_BLEND_ONE; desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; device->CreateBlendState(&desc, &states[Additive]); // 4. Multiplicative: 곱셈 블렌딩 (어두운 효과) desc.RenderTarget[0].SrcBlend = D3D11_BLEND_DEST_COLOR; desc.RenderTarget[0].DestBlend = D3D11_BLEND_ZERO; desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; device->CreateBlendState(&desc, &states[Multiplicative]); // 5. NonPremultiplied: 비사전곱셈 알파 desc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE; desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; device->CreateBlendState(&desc, &states[NonPremultiplied]); }
C++
복사

4. Depth/Stencil State (깊이/스텐실 상태)

깊이/스텐실 버퍼의 역할

Depth Buffer (깊이 버퍼, Z-Buffer)
각 픽셀의 깊이(카메라로부터의 거리) 저장
가까운 물체가 먼 물체를 가리도록 보장 (Hidden Surface Removal)
0.0(가까움) ~ 1.0(멀리) 범위
Stencil Buffer (스텐실 버퍼)
각 픽셀에 정수 값(보통 8bit) 저장
마스킹, 아웃라인, 그림자 볼륨 등에 활용
조건부 렌더링 구현

Depth/Stencil 처리 흐름

처리 순서
1.
Stencil Test: 스텐실 버퍼 값 비교
2.
Depth Test: 깊이 버퍼 값 비교
3.
Pixel Shader: 테스트 통과 시 픽셀 셰이더 실행
4.
Stencil Update: 결과에 따라 스텐실 버퍼 업데이트
5.
Depth Update: 깊이 버퍼 업데이트 (옵션)

Depth/Stencil State 생성

// Depth/Stencil State 설정 구조체 D3D11_DEPTH_STENCIL_DESC dsDesc = {}; // Depth Test 설정 dsDesc.DepthEnable = TRUE; // 깊이 테스트 활성화 dsDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL; // 깊이 쓰기 활성화 dsDesc.DepthFunc = D3D11_COMPARISON_LESS; // 가까운 것을 그림 // Stencil Test 설정 dsDesc.StencilEnable = FALSE; // 스텐실 테스트 비활성화 dsDesc.StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK; // 0xFF dsDesc.StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK; // 0xFF // 앞면(Front-facing) 폴리곤에 대한 스텐실 동작 dsDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP; // 스텐실 실패 시 dsDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP; // 깊이 실패 시 dsDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP; // 통과 시 dsDesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS; // 항상 통과 // 뒷면(Back-facing) 폴리곤에 대한 스텐실 동작 dsDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP; dsDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP; dsDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP; dsDesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS; // Depth/Stencil State 객체 생성 ID3D11DepthStencilState* depthStencilState = nullptr; device->CreateDepthStencilState(&dsDesc, &depthStencilState);
C++
복사

주요 속성 상세 설명

DepthEnable
TRUE: 깊이 테스트 활성화 (일반적)
FALSE: 깊이 테스트 비활성화 (UI, 스카이박스 등)
DepthWriteMask
enum D3D11_DEPTH_WRITE_MASK { D3D11_DEPTH_WRITE_MASK_ZERO, // 깊이 쓰기 비활성화 D3D11_DEPTH_WRITE_MASK_ALL // 깊이 쓰기 활성화 };
C++
복사
ALL: 깊이 값을 버퍼에 기록 (일반적)
ZERO: 깊이 읽기만, 쓰기는 안 함 (반투명 렌더링, 파티클)
DepthFunc (깊이 비교 함수)
enum D3D11_COMPARISON_FUNC { D3D11_COMPARISON_NEVER, // 항상 실패 D3D11_COMPARISON_LESS, // New < Old (일반적) D3D11_COMPARISON_EQUAL, // New == Old D3D11_COMPARISON_LESS_EQUAL, // New <= Old D3D11_COMPARISON_GREATER, // New > Old D3D11_COMPARISON_NOT_EQUAL, // New != Old D3D11_COMPARISON_GREATER_EQUAL, // New >= Old D3D11_COMPARISON_ALWAYS // 항상 통과 };
C++
복사
LESS: 가까운 픽셀이 먼 픽셀을 덮음 (가장 일반적)
LESS_EQUAL: LESS + 같은 깊이도 통과 (Z-Fighting 방지)
StencilEnable
TRUE: 스텐실 테스트 활성화
FALSE: 스텐실 테스트 비활성화 (일반적)
StencilReadMask & StencilWriteMask
스텐실 버퍼 읽기/쓰기 시 AND 연산할 마스크
특정 비트만 사용하고 싶을 때 활용
기본값: 0xFF (모든 비트 사용)
Stencil Operations
enum D3D11_STENCIL_OP { D3D11_STENCIL_OP_KEEP, // 유지 D3D11_STENCIL_OP_ZERO, // 0으로 설정 D3D11_STENCIL_OP_REPLACE, // 참조값으로 대체 D3D11_STENCIL_OP_INCR_SAT, // +1 (포화) D3D11_STENCIL_OP_DECR_SAT, // -1 (포화) D3D11_STENCIL_OP_INVERT, // 비트 반전 D3D11_STENCIL_OP_INCR, // +1 (래핑) D3D11_STENCIL_OP_DECR // -1 (래핑) };
C++
복사
Front/BackFace 구조체
struct D3D11_DEPTH_STENCILOP_DESC { D3D11_STENCIL_OP StencilFailOp; // 스텐실 테스트 실패 시 D3D11_STENCIL_OP StencilDepthFailOp; // 스텐실 통과, 깊이 실패 시 D3D11_STENCIL_OP StencilPassOp; // 둘 다 통과 시 D3D11_COMPARISON_FUNC StencilFunc; // 스텐실 비교 함수 };
C++
복사

기본 Depth/Stencil State 값

기본값 특징
DepthEnable = TRUE: 깊이 테스트 활성화
DepthWriteMask = ALL: 깊이 쓰기 활성화
DepthFunc = LESS: 가까운 것이 앞에
StencilEnable = FALSE: 스텐실 비활성화

Depth/Stencil State 바인딩

// Stencil Reference Value: 스텐실 비교 및 REPLACE 연산에 사용 UINT stencilRef = 1; // Output Merger에 바인딩 context->OMSetDepthStencilState(depthStencilState, stencilRef);
C++
복사

실전 Depth/Stencil State 패턴

enum class eDepthStencilState { Default, // 일반 3D 렌더링 DepthReadOnly, // 깊이 읽기만 (파티클, 반투명) NoDepthTest, // 깊이 테스트 없음 (UI, 스카이박스) StencilWrite, // 스텐실 쓰기 (마스크 생성) StencilRead, // 스텐실 읽기 (마스크 적용) Count }; void InitializeDepthStencilStates() { D3D11_DEPTH_STENCIL_DESC desc = {}; // 1. Default: 일반 3D 렌더링 desc.DepthEnable = TRUE; desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL; desc.DepthFunc = D3D11_COMPARISON_LESS; desc.StencilEnable = FALSE; device->CreateDepthStencilState(&desc, &states[Default]); // 2. DepthReadOnly: 깊이 읽기만 (반투명 오브젝트) desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO; device->CreateDepthStencilState(&desc, &states[DepthReadOnly]); // 3. NoDepthTest: 깊이 테스트 없음 (UI) desc.DepthEnable = FALSE; desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO; device->CreateDepthStencilState(&desc, &states[NoDepthTest]); // 4. StencilWrite: 스텐실 버퍼에 마스크 작성 desc.DepthEnable = TRUE; desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL; desc.StencilEnable = TRUE; desc.StencilWriteMask = 0xFF; desc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS; desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_REPLACE; // 참조값으로 대체 desc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP; desc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP; desc.BackFace = desc.FrontFace; device->CreateDepthStencilState(&desc, &states[StencilWrite]); // 5. StencilRead: 스텐실 마스크 영역만 렌더링 desc.FrontFace.StencilFunc = D3D11_COMPARISON_EQUAL; // 참조값과 같으면 통과 desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP; // 유지 device->CreateDepthStencilState(&desc, &states[StencilRead]); }
C++
복사

5. 실전 활용 예제

투명 오브젝트 렌더링

void RenderTransparentObjects(const std::vector<GameObject*>& transparentObjects) { // 1. 불투명 오브젝트를 먼저 렌더링 (일반 State) RenderOpaqueObjects(); // 2. 투명 오브젝트는 깊이 쓰기 비활성화 + 알파 블렌딩 BindDepthStencilState(eDepthStencilState::DepthReadOnly); BindBlendState(eBlendState::AlphaBlend); // 3. 뒤에서 앞으로 정렬 (Painter's Algorithm) std::vector<GameObject*> sorted = transparentObjects; SortBackToFront(sorted, camera->GetPosition()); // 4. 양면 렌더링 (투명 오브젝트는 뒷면도 보임) BindRasterizerState(eRasterizerState::SolidNone); // 5. 렌더링 for (auto& obj : sorted) { obj->Render(); } // 6. State 복원 BindDepthStencilState(eDepthStencilState::Default); BindBlendState(eBlendState::Default); BindRasterizerState(eRasterizerState::SolidBack); }
C++
복사

파티클 시스템

void RenderParticles(ParticleSystem* particles) { // 가산 블렌딩 (밝게 겹침) BindBlendState(eBlendState::Additive); // 깊이 읽기만 (뒤에 가려지되, 깊이는 쓰지 않음) BindDepthStencilState(eDepthStencilState::DepthReadOnly); // 양면 렌더링 BindRasterizerState(eRasterizerState::SolidNone); // 파티클 렌더링 particles->Render(); // State 복원 BindBlendState(eBlendState::Default); BindDepthStencilState(eDepthStencilState::Default); BindRasterizerState(eRasterizerState::SolidBack); }
C++
복사

스텐실 기반 아웃라인

void RenderObjectWithOutline(GameObject* obj, Color outlineColor) { // 1단계: 스텐실 버퍼에 오브젝트 마스크 작성 BindDepthStencilState(eDepthStencilState::StencilWrite); context->OMSetDepthStencilState(states[StencilWrite].Get(), 1); // Ref = 1 obj->Render(); // 2단계: 약간 확대된 오브젝트를 스텐실 마스크 밖에만 렌더링 BindDepthStencilState(eDepthStencilState::StencilRead); context->OMSetDepthStencilState(states[StencilRead].Get(), 0); // Ref = 0 (1과 다른 영역) // 스케일 약간 증가 obj->GetTransform()->SetScale(obj->GetScale() * 1.05f); // 단색 셰이더로 아웃라인 렌더링 Shader* outlineShader = Resources::Find<Shader>(L"OutlineShader"); outlineShader->SetColor(outlineColor); outlineShader->Bind(); obj->Render(); // 3단계: 원래대로 복원 obj->GetTransform()->SetScale(obj->GetScale() / 1.05f); BindDepthStencilState(eDepthStencilState::Default); }
C++
복사

UI 렌더링

void RenderUI() { // 깊이 테스트 비활성화 (항상 최상단에 표시) BindDepthStencilState(eDepthStencilState::NoDepthTest); // 알파 블렌딩 BindBlendState(eBlendState::AlphaBlend); // 양면 렌더링 (UI는 보통 평면) BindRasterizerState(eRasterizerState::SolidNone); // UI 요소들 렌더링 for (auto& uiElement : uiElements) { uiElement->Render(); } // State 복원 BindDepthStencilState(eDepthStencilState::Default); BindBlendState(eBlendState::Default); BindRasterizerState(eRasterizerState::SolidBack); }
C++
복사

6. 성능 최적화

State 변경 최소화

State 변경 비용
State 변경은 GPU 파이프라인 플러시를 유발
가능한 한 같은 State를 사용하는 오브젝트를 함께 렌더링
// 나쁜 예: State를 자주 변경 for (auto& obj : allObjects) { obj->BindStates(); // 매번 State 변경 obj->Render(); } // 좋은 예: State별로 그룹화 for (auto state : allStates) { BindState(state); for (auto& obj : objectsByState[state]) { obj->Render(); // State 변경 없음 } }
C++
복사

투명도 정렬 최적화

// 불투명 오브젝트: 앞에서 뒤로 정렬 (Early-Z 최적화) SortFrontToBack(opaqueObjects, cameraPos); // 투명 오브젝트: 뒤에서 앞으로 정렬 (정확한 블렌딩) SortBackToFront(transparentObjects, cameraPos);
C++
복사

깊이 프리패스 (Depth Pre-pass)

void RenderWithDepthPrepass() { // 1단계: 깊이만 렌더링 (색상 쓰기 비활성화) D3D11_BLEND_DESC noColorWrite = {}; noColorWrite.RenderTarget[0].RenderTargetWriteMask = 0; // 색상 쓰기 안 함 BindBlendState(noColorWriteState); RenderOpaqueObjects(); // 2단계: 깊이 읽기 전용으로 색상 렌더링 BindDepthStencilState(eDepthStencilState::DepthReadOnly); BindBlendState(eBlendState::Default); RenderOpaqueObjects(); // 픽셀 셰이더는 앞면만 실행됨 }
C++
복사

결론

Rasterizer State, Blend State, Depth/Stencil State는 DirectX 11 렌더링 파이프라인의 출력 단계를 제어하는 핵심 상태 객체들입니다.

핵심 요약

Rasterizer State
폴리곤이 픽셀로 변환되는 방식 제어
컬링, 클리핑, 와이어프레임 등 설정
렌더링 품질과 성능의 균형
Blend State
새 픽셀과 기존 픽셀의 혼합 방식
투명도, 빛 효과, 특수 효과 구현
알파 블렌딩, 가산 블렌딩 등 다양한 패턴
Depth/Stencil State
깊이 테스트로 가시성 판단
스텐실 버퍼로 고급 효과 구현
아웃라인, 그림자, 반사 등 활용

실전 적용 지침

State 설계
필요한 모든 State를 초기화 시 미리 생성
런타임에는 바인딩만 수행 (생성 비용 절약)
열거형으로 State를 관리하여 가독성 향상
렌더링 순서
1.
불투명 오브젝트 (앞→뒤 정렬)
2.
스카이박스 (깊이 테스트 비활성화)
3.
투명 오브젝트 (뒤→앞 정렬)
4.
파티클 (가산 블렌딩)
5.
UI (깊이 테스트 없음)
디버깅 팁
와이어프레임 모드로 지오메트리 확인
깊이 버퍼를 텍스처로 시각화
State 변경 횟수 프로파일링
이 세 가지 State를 마스터하면 대부분의 렌더링 효과를 구현할 수 있으며, 고급 기법으로 나아갈 수 있는 탄탄한 기반을 갖추게 됩니다.