[PBR] HDRI - postprocess
우리가 후처리를 할 때 예를들어 가우시안 블러를 적용할 때, LDR(Low Dynamic Range)이미지(unorm)이미지로 적용을 하였습니다. 하지만, HDR(High Dynamic Range)이미지를 적용할 때는 방법이 다릅니다. 더 사실적으로 표현하기 위함이죠.
HDR이미지는 unorm(R8G8B8A8)과 다르게 float16(R16G16B16A16)입니다. 더 넓은 범위의 빛의 강도를 조절할 수 있지요. 가우시안 블러를 적용하려면 다음과 같은 단계를 따릅니다.
1. 해상도 줄이기(빛 번짐 표현 하기 위함. 이거 없으면 가우시안 블러를 많이 적용해야돼서 속도에 영향을 미침)
=> Down 샘플링과 Up샘플링으로 표현
2. 가우시안 블러(주위 값을 Convolusion을리가 후처리를 할 때 예를들어 가우시안 블러를 적용할 때, LDR이미지(unorm)이미지로 적용을 하였습니다.
하지만, HDR이미지를 적용할 때는 방법이 다릅니다. 더 사실적으로 표현하기 위함이죠.
HDR이미지는 unorm(R8G8B8A8)과 다르게 float16(R16G16B16A16)입니다. 더 넓은 범위의 빛의 강도를 조절할 수 있지요. 가우시안 블러를 적용하려면 다음과 같은 단계를 따릅니다.
1. 해상도 줄이기(빛 번짐 표현 하기 위함. 이거 없으면 가우시안 블러를 많이 적용해야돼서 속도에 영향을 미침)
=> Down 샘플링과 Up샘플링으로 표현
2. 가우시안 블러(convolution을 통해 주위 값 계산)
3. original target과 블러된 값 합침
여기서 LDR방식과 HDR방식의 블러 처리 방식이 다릅니다.
1. HDR방식은 맨 마지막 Combine(두 타겟 합치는 과정)에서 최종적으로 float형식을 모니터로 내보내야 하기 때문에 Unorm으로 바꿔줍니다. 이 때, Tone Mapping과정이 필요합니다.(gamma correction포함)
2. HDR은 LDR과 다르게 해상도 Down, Up과정에서 모두 블러 처리를 합니다. X축 Y축 따로 하는게 아니라 그냥 한번에 다 다같이 합니다.
공식은 다음과 같습니다.
DownSampling
=>
float4 main(SamplingPixelShaderInput input) : SV_TARGET
{
float3 a = g_texture0.Sample(g_sampler, input.texcoord + float2(-2*dx,2*dy)).rgb;
float3 b = g_texture0.Sample(g_sampler, input.texcoord + float2(0.0, 2 * dy)).rgb;
float3 c = g_texture0.Sample(g_sampler, input.texcoord + float2(2 * dx, 2 * dy)).rgb;
float3 d = g_texture0.Sample(g_sampler, input.texcoord + float2(-2 * dx,0.0)).rgb;
float3 e = g_texture0.Sample(g_sampler, input.texcoord + float2(0.0, 0.0)).rgb;
float3 f = g_texture0.Sample(g_sampler, input.texcoord + float2(2 * dx, 0.0)).rgb;
float3 g = g_texture0.Sample(g_sampler, input.texcoord + float2(-2 * dx, -2 * dy)).rgb;
float3 h = g_texture0.Sample(g_sampler, input.texcoord + float2(0.0, -2 * dy)).rgb;
float3 i = g_texture0.Sample(g_sampler, input.texcoord + float2(2 * dx, -2 * dy)).rgb;
float3 j = g_texture0.Sample(g_sampler, input.texcoord + float2(-dx, dy)).rgb;
float3 k = g_texture0.Sample(g_sampler, input.texcoord + float2(dx, dy)).rgb;
float3 l = g_texture0.Sample(g_sampler, input.texcoord + float2(-dx, - dy)).rgb;
float3 m = g_texture0.Sample(g_sampler, input.texcoord + float2(dx, -dy)).rgb;
float3 downSample = e * 0.125;
downSample += (a + c + g + i) * 0.03125;
downSample += (b + d + f + h) * 0.0625;
downSample += (j + k + l + m) * 0.125;
return float4(downSample, 1.0);
}
UpSampling
=>
float4 main(SamplingPixelShaderInput input) : SV_TARGET
{
float3 a = g_texture0.Sample(g_sampler, input.texcoord + float2(-dx, dy)).rgb;
float3 b = g_texture0.Sample(g_sampler, input.texcoord + float2(0.0, dy)).rgb;
float3 c = g_texture0.Sample(g_sampler, input.texcoord + float2(dx, dy)).rgb;
float3 d = g_texture0.Sample(g_sampler, input.texcoord + float2(-dx, 0.0)).rgb;
float3 e = g_texture0.Sample(g_sampler, input.texcoord + float2(0.0, 0.0)).rgb;
float3 f = g_texture0.Sample(g_sampler, input.texcoord + float2(dx, 0.0)).rgb;
float3 g = g_texture0.Sample(g_sampler, input.texcoord + float2(-dx, -dy)).rgb;
float3 h = g_texture0.Sample(g_sampler, input.texcoord + float2(0.0, -dy)).rgb;
float3 i = g_texture0.Sample(g_sampler, input.texcoord + float2(dx, -dy)).rgb;
float3 upSample = e * 4.0;
upSample += (b + d + f + h) * 2.0;
upSample += (a + c + g + i);
upSample *= 1.0 / 16.0;
return float4(upSample, 1.0);
}
3. 두 Target합칠 때 코드 비교입니다.
//LDR
float4 main(SamplingPixelShaderInput input) : SV_TARGET
{
return g_texture0.Sample(g_sampler, input.texcoord) + strength * g_texture1.Sample(g_sampler, input.texcoord);
}
//HDR
float4 main(SamplingPixelShaderInput input) : SV_TARGET
{
float3 color0 = g_texture0.Sample(g_sampler, input.texcoord).rgb;
float3 color1 = g_texture1.Sample(g_sampler, input.texcoord).rgb;
float3 combined = (1.0 - strength) * color0 + strength * color1;
// Tone Mapping
combined = LinearToneMapping(combined);
//여기까진 float16형식이었다가, 반환은 백버퍼에 하기때문에 unorm으로 됨
return float4(combined, 1.0f);
}
즉, LDR은 블러 Target에 strength를 곱해서 Original이미지에 더해주는 반면, HDR은 각 값을 interpolation합니다. 빛나는 부분은 약간의 빛번짐이 있다는 걸 고려해서, 살짝 뿌옇게 만들어주는 방식입니다. LDR에 interpolation을 하면 전체적으로 이미지가 뿌해지기만 하는 효과가 있어서 방식이 다릅니다.
참고 자료: phys. Based Bloom
https://learnopengl.com/Guest-Articles/2022/Phys.-Based-Bloom
강의 출처 : 홍정모 그래픽스 새싹코드 Part3
https://www.honglab.ai/courses/take/graphicspt3