MOONSUN
[D3D] IBL 구현 이슈 : IBL Specular가 Roughness에 반응하지 않았던 이유 본문
PBR + IBL 환경을 구현 한 후, 머티리얼 값을 수동으로 조절하면서 구현이 잘 되었는지 확인중이었는데, 이상한 현상 발견.

문제 상황 : IBL Specular가 거칠기에 따라 뭉게지지 않는 문제
- Metallic = 1
- Roughness를 0 → 1로 점점 증가
정상적인 경우라면 거칠기가 커질수록 환경 반사가 점점 뭉게지고 흐려져야 하는데,
반사가 뭉게지지 않고, 색이 조금 밝아질 뿐 거칠기 변화가 시각적으로 거의 느껴지지 않음
즉, 물리적으로 올바르지 않은 IBL Specular 결과가 나오고 있었다.
셰이더 코드를 잘못 작성한줄 알고 며칠동안 들여다 봤지만, 원인을 발견 못했는데,,
설마하고 샘플러 초기화 부분을 확인해봤는데
샘플러 초기화가 잘못되고 있었음..!!
내가 사용 중이던 샘플러 코드는 아래와 같다
// 샘플러 (s2)
D3D11_SAMPLER_DESC sampIBL = {};
sampIBL.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; // 선현보간으로 수행
sampIBL.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
sampIBL.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
sampIBL.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
HR_T(m_D3DDevice.GetDevice()->CreateSamplerState(&sampIBL, m_pSamplerIBL.GetAddressOf()));
// IBL CLAMP 샘플러 (s3)
D3D11_SAMPLER_DESC sampClamp = {};
sampClamp.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
sampClamp.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
sampClamp.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
sampClamp.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
HR_T(m_D3DDevice.GetDevice()->CreateSamplerState(&sampClamp, m_pSamplerIBL_Clamp.GetAddressOf()));
겉보기엔 전혀 문제 없어 보였지만, D3D11_SAMPLER_DESC의 {} 초기화가 문제였다.
D3D11_SAMPLER_DESC sampIBL = {};
이렇게 선언하는 순간, 구조체의 모든 필드가 0으로 초기화된다.
그러면 아래 필드들이 기본 값으로 채워짐
FLOAT MinLOD; // default: 0 (NO!!!)
FLOAT MaxLOD; // default: 0 (NO!!!)
FLOAT MipLODBias; // default: 0 (OK)
즉, 사실상 아래로 초기화 하고 있었던 것....
MinLOD = 0
MaxLOD = 0 // ← 이게 핵심
IBL Specular는 보통 다음과 같이 거칠기에 따라 MIP Level을 직접 지정하는데,
SampleLevel(prefilterMap, R, roughness * mipCount);
하지만 샘플러 단계에서 LOD가 강제로 clamp된다.
finalMip = clamp(requestedMip, MinLOD, MaxLOD)
= clamp(anything, 0, 0)
= 0
이렇게 되면 결과적으로
어떤 roughness 값이 와도, 항상 MIP 0만 샘플링
prefiltered cubemap의 뭉개진 mip들이 전혀 사용되지 않는다..
그래서 거칠기를 최대로 올려도 반사가 흐려지지 않고, 오히려 에너지가 과도하게 남아 하얗게 보이는 현상 발생했던 것..
결과적으로 아래 처럼 LOD 범위를 명시적으로 지정 해야한다.
// 샘플러 (s2)
D3D11_SAMPLER_DESC sampIBL = {};
sampIBL.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; // 선형보간으로 수행
sampIBL.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
sampIBL.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
sampIBL.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
sampIBL.MinLOD = 0.0f; // 최소/최대 MIP 레벨 지정
sampIBL.MaxLOD = D3D11_FLOAT32_MAX;
sampIBL.MipLODBias = 0.0f;
HR_T(m_D3DDevice.GetDevice()->CreateSamplerState(&sampIBL, m_pSamplerIBL.GetAddressOf()));
// IBL CLAMP 샘플러 (s3)
D3D11_SAMPLER_DESC sampClamp = {};
sampClamp.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
sampClamp.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
sampClamp.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
sampClamp.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
sampClamp.MinLOD = 0.0f;
sampClamp.MaxLOD = D3D11_FLOAT32_MAX;
sampClamp.MipLODBias = 0.0f;
HR_T(m_D3DDevice.GetDevice()->CreateSamplerState(&sampClamp, m_pSamplerIBL_Clamp.GetAddressOf()));
이제 물리적으로 올바른 IBL Specular가 동작하는 걸 확인할 수 있다..!



구현 내용은 아래 깃허브에..!
https://github.com/MoonSun-v/D3DProgramming/tree/main/D3DProgramming/15.IBL
'D3D' 카테고리의 다른 글
| [그래픽스] Procedural Noise의 이해 : Noise → FBM → Domain Warping (0) | 2025.12.18 |
|---|---|
| [그래픽스] HDR 렌더링 : 렌더타겟, 노출, 톤 매핑 (0) | 2025.12.17 |
| [그래픽스] IBL (Image Based Lighting) : PBR을 위한 환경 기반 간접광 (0) | 2025.12.10 |
| [그래픽스] PBR(PBR Rendering)에서의 감마 보정 (0) | 2025.12.03 |
| [그래픽스] PBR 이론 및 구현 : BSDF, BRDF, Cook–Torrance (0) | 2025.12.02 |