프로그래밍 2019. 12. 2. 15:59
728x90

 

hdr 의 luminance map(휘도맵)에 대해

쉽고, 깔끔하게 정리해놓은 슬라이드다

꼭, 읽어보자

 

https://www.slideshare.net/youpyo/hdr-8480350

 

D2 Hdr

H D RHigh Dynamic Range김정희최유표

www.slideshare.net

 

일단, 휘도맵은 orig 화면 전체의 대표밝기( luminance )를 구하는 것인데,

 

orig 화면을   128 -> 64 -> 32 -> 16 -> 8 -> 4 -> 2 -> 1 크기로

다운샘플링해서 만들수 있다

 

(즉, luminance 제작용 렌더타겟 텍스쳐 8개가 필요하다,

당연히 텍스쳐 사이즈는 128, 64, 32 ... 1 이 될것이고, 

추가적으로,

이전프레임의 휘도를 저장할 사이즈 1 짜리의 텍스쳐가 하나더 필요하다

 

휘도맵은 값이 기록될때 exp 계산이 되므로, 값이  1 이상일수 있을것이라 생각하여,

D3DFMT_A16B16G16R16F 포맷을 사용하였는데,

*혹시 틀렸으면 지적부탁드립니다

 

참, 나의 경우 dx9 를 사용중이라, ps 에서 float4 로만 컬러값을 리턴할수 있어서

D3DFMT_R16F 포맷을 사용할수가 없는데, float 로 컬러값을 리턴할수 있으면,

D3DFMT_R16F 포맷을 사용하여, 텍스쳐 메모리 용량을 줄일수 있을것이다

)

 

위, 슬라이드에 보면, 공식적인 luminance 를 만드는, 쉐이더 코드가 있는데,

주의할 점이 있다,

 

128 x 128 맵에서, 

fLogLumSum += log( dot( rgb, LUMI_VEC ... 

색상값을 휘도로 변경

 

중간 단계의 맵들에서는

기본적인 샘플링을 거치고,

 

마지막, 1x1 맵에서는

샘플링과 함께,

fsample = exp ( fsample / 16 );

exp 을 적용해주어야 정확한 결과를 볼수있다

 

log 로 값의 볼륨을 줄여놓고, exp 로 값을 원복시키는 것인데,

log, exp 를 빼고 테스트를 해보면, 밝기의 세부적인 단계가 떨어져서, 화면이 뭉그러져 보인다,

큰 값 그대로를 다운샘플링하는것보다, 작게 만들어 다운샘플링하면, 

값오차가 줄어들 것이고, 이를 다시 원복하면, 좀더 세밀한 값들을 얻어오는??

뭐 그러한 이치로 추측이 된다

 

어쨋든,

log, exp 사용이 정확하지 않을때는,

매우 어두운 값이 출력될 것이다 ( 나처럼,,, ㅎㅎ )

 

 

lumi 는 보통, 이전프레임의 lumi 와 현재의 lumi 값

두개를 가져와서, 여러가지 공식으로 사용할수 있고, (뭐 노출시뮬레이션이라든지)

 

일단은 심플하게,

 

fAverLumi = (old * 0.5 + new * 0.5) 로 구한값을 out.rgb 에 곱해서 출력해보니,

Out.rgb *= ( fAverLumi );

 

 

오옷!

 

낮게 셋팅한 전체 디퓨즈, 앰비언트 값만큼 전체적으로 어두워진 화면이 출력되었다

 

 

hdr 톤맵, 감마 적용

 

hdr 톤맵, 감마, 휘도 적용

 

sdr 화면 (아무것도 하지않은, 예전의)

 

 

단순히, rgb 값을 낮추는 것은, 특정 상수값을 곱해주는것만으로도 쉽게 할수있지만,

휘도맵을 사용하면, 항상 장면의 대표밝기를 새롭게 계산해서 적용하는것이니까,

 

rgb *= 장면 밝기

 

극단적으로 어두운 곳은 더욱 어둡게, 극단적으로 밝은 곳은 더욱 밝게

색의 표현범위가 넓어진다고 볼수있다

물론, 이 넓어진 색범위를 최종적으로는 톤매핑으로 정리를 해야,

색이 뭉치지않게 출력이 되는 것이고

 

 

으흠~~

 

 

휘도맵을 사용하는 공식을 이용하여, 좀더 정확하게

장면의 luminance 를 적용해 보았다

 

코드를 살펴보자,

 

 

static const float3 LUMINANCE_VECTOR = float3(0.2125f, 0.7154f, 0.0721f);

 

//fSceneLumi 의 범위내로 밝기를 맞추어주는 코드

fSceneLumi  = (old * 0.5 + new * 0.5)
float fMiddleGrayOfSceneLumi = 1.03 - ( 2 / (2 + log10(fSceneLumi + 1)) );
float fRgbLumi = dot( Out.rgb, LUMINANCE_VECTOR ) + 0.0001f;

//rgb lumi 를 fSceneLumi 의 미들그레이로 감쇄하여,

//fSceneLumi 값범위내로 조절해준다
float fLumiScaled = (fRgbLumi * fMiddleGrayOfSceneLumi) / (fSceneLumi + 0.0001f);

float fWhite = 1;
//scale all luminance within a displayable range of 0 to 1
fLumiScaled = (fLumiScaled * (1.0f + fLumiScaled / (fWhite))) / (1.0f + fLumiScaled);

//
Out.rgb *= (fLumiScaled) * _vToneMapVal.x; //밝기 조정

 

 

우리가 구한 fSceneLumi 값 범위내로 휘도를 적용하는 코드이다,

휘도계산을 위해서 out.rgb 역시 휘도값으로 만들어두고,

fSceneLumi 의 미들그레이( 실제 이값은 fSceneLumi 의 딱반값이 아니라, 

공식으로 감쇄된, 인간의 눈에 맞추어서 그레이존이 좀더 아래쪽에 위치하는

결론적으로는 fSceneLumi 보다 많이 작은 값이다 )를 구해둔다

 

결국, 밝기를 fSceneLumi 범위내로 만들어주기 위해서,  

float fLumiScaled = (fRgbLumi * fMiddleGrayOfSceneLumi) / (fSceneLumi + 0.0001f); 

이 식이 들어간다

 

Out.rgb 에는 최종적으로, fSceneLumi 범위내로 조정된 자신의 휘도를 곱해주었다

(fSceneLumi 는 전체화면 휘도의 중간치이므로, out.rgb 를 이 중간치 범위내에 맞추면,

원래화면보다, 화면이 더 어두워진다)

 

 

심플하게 처리한거 (Out.rgb *= fSceneLumi)

 

 

위 식대로 휘도스케일을 구해서 적용한거 (Out.rgb  *= fLumiScaled)

 

 

전체적으로 설정된 디퓨즈값, 앰비언트의 적용치보다

좀더 어둡게 출력된것으로 보여진다

어쨋든, 밝기는 외부변수를 추가하여, 다시 조정할수 있는 부분이고, 

 

심플하게 처리했던 때는,

맵을 돌아다닐때 실시간으로 화면의 색감이 휙휙 바뀌어

눈에 거슬리는 부분이 많았는데,

 

lumiscale 을 구해서 적용해보니, 화면의 색감이 휙휙 바뀌는 부분이

잘 눈에 띄지 않았다

 

 

으흠~~

 

매우 어둡지만, 나름 분위기있는 색감이 표현되는 것 같다

 

이제, hdr 에서 꼭 다루어지는, bloom 에 대해서 알아보자

다음장으로~~~

 

728x90
posted by BK dddDang
: