이번 글은
기본적으로 그림자 매핑 방식에 대해
이해하고 있어야 한다.
[DirectX11] 그림자 매핑 (Shadow Mapping)
멀티 패스 렌더링 (Multi-Pass Rendering) 기본적으로 멀티 패스 렌더링이란 하나의 장면(Scene)을 완성하기 위해 여러 단계(Pass)로 나누어 렌더링하는 기법을 말한다. 여기서 패스(Pass) 는 한 번의 DrawCall
wooj22.tistory.com
그림자 팩터 (Shadow Factor)
그림자 팩터(Shadow Factor)는 픽셀이 광원에서 차단되는 정도를 나타내는 0~1 사이의 값이다.
| 값 | 의미 |
| 0 | 그림자에 완전히 가려짐 → direct light 없음 |
| 1 | 완전히 광원에 노출됨 → direct light 완전 적용 |
| 0~1 | 반쯤 그림자(Soft Shadow) → direct light 일부 적용 |
PCF (Percentage Closer Filtering)
이전 글에서 설명한 PixelShader는 shadowFactor를 0아니면 1로 처리하고 있어 그림자 음영에 단계가 없이 딱딱하게 처리된다. 이때 PCF방식을 사용하면 부드러운 그림자 처리가 가능하다.
PCF(Percentage closer Filtering)는 단순히 한 점에서 깊이를 비교하는 대신, 그림자맵 주변 여러 샘플을 참조하여 각각의 비교 결과를 평균 내는 확률적 필터링 기법이다. 즉, 그림자맵 샘플링 시 주변 texel들을 여러 번 샘플링하고, 각 결과를 더해 평균값을 내어 최종 그림자 팩터(Shadow Factor)로 사용한다. 이때 Shadow Factor는 0~1 사이의 연속적인 값이 되어, 그림자 경계가 부드럽고 자연스럽게 표현된다.
DirectX에서는하드웨어 레벨 PCF를 수행하는 SamplerComparisonState 샘플러를 지원한다.
ID3D11SamplerState* samplerComparison = nullptr;
sampDesc = {};
sampDesc.Filter = D3D11_FILTER_COMPARISON_MIN_MAG_MIP_LINEAR;
sampDesc.ComparisonFunc = D3D11_COMPARISON_LESS_EQUAL;
sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
HR_T(device->CreateSamplerState(&sampDesc, samplerComparison));
이 샘플러를 사용하면 GPU가 내부적으로 주변 texel(최대 4개)을 bilinear 필터링한 깊이값과 currentShadowDepth를 비교하여, 그 결과를 0~1 사이의 부드러운 실수값으로 반환한다. 즉, 한 번의 샘플링(SampleCmpLevelZero)으로 이미 하드웨어 PCF가 내장된 결과값을 얻을 수 있다. 아래 코드는 9개의 texel 오프셋을 수동으로 지정하여 9회 하드웨어 PCF를 수행하고, 그 평균을 통해 더욱 부드러운 shadowFactor를 계산하는 예시이다.
Texture2D shadowMap : register(t6);
SamplerComparisonState samplerComparison : register(s1);
float4 main(PS_INPUT input) : SV_TARGET
{
float shadowFactor = 1.0f;
// 1. Current Shadow Depth
float currentShadowDepth = input.posShadow.z / input.posShadow.w;
// Shadow map UV (0~1)
float2 uv = input.posShadow.xy / input.posShadow.w;
uv.y = -uv.y;
uv = uv * 0.5f + 0.5f;
// 2. ShadowMap Depth
if(uv.x >= 0.0 && uv.x <= 1.0 && uv.y >= 0.0 && uv.y <= 1.0)
{
float2 offsets[9] = {
float2(-1, -1), float2(0, -1), float2(1, -1),
float2(-1, 0), float2(0, 0), float2(1, 0),
float2(-1, 1), float2(0, 1), float2(1, 1)
};
float2 texelSize = 1.0 / ShadowMapSize; // 텍셀 크기 (ShadowMap 해상도 기준)
shadowFactor = 0.0f;
// ⭐ PCF - 9 texel 평균으로 그림자 팩터 계산
[unroll]
for(int i=0; i<9; i++)
{
float2 sampleUV = uv + offsets[i] * texelSize;
shadowFactor += shadowMap.SampleCmpLevelZero(samplerComparison, sampleUV, currentShadowDepth - 0.001);
}
shadowFactor = shadowFactor / 9.0f;
}
...기타 텍스처맵핑 및 라이트 계산 생략
float final = (directLighting * shadowFactor) + ambientLighting + emissive;
return float4(final.xxx, opacity);
}'Programming > 컴퓨터그래픽스 (DX 11)' 카테고리의 다른 글
| [PBR] 1. 빛 (0) | 2025.12.04 |
|---|---|
| [PBR] PBR이란 무엇인가? (1) | 2025.12.04 |
| [DirectX11] 그림자 매핑 (Shadow Mapping) (0) | 2025.11.11 |
| [Shader] 외곽선(OutLine) 패스 (0) | 2025.11.10 |
| [Shader] 툰 쉐이딩(Toon Shading)과 램프텍스처(RampTexture) (2) | 2025.11.10 |