티스토리 뷰

Game/MMD

Tone mapping (3)

newpolaris 2017. 9. 22. 21:17

이전에는 사진에서 쓰이는 톤매핑 구현하다(했다가 그만 뒀는데) 치웠는데

검색어를 바꾸니 더 나오네;

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

https://www.slideshare.net/cagetu/ndc11-hdr

40 페이지 부터 언급이 되어있다.

42 페이지 Reinhard 가 나온다.

그 다음이야기가 아래에서 이어진다.

http://egloos.zum.com/cagetu/v/6198009

우선 이걸 기반으로 ModelViewer를 해석해보자.

ToneMappingUtility.hlsli 에서

float3 TM_Reinhard(float3 hdr, float k = 1.0)
{
    return hdr / (hdr + k);
}

// The next generation of filmic tone operators.
float3 ToneMapACES( float3 hdr )
{
    const float A = 2.51, B = 0.03, C = 2.43, D = 0.59, E = 0.14;
    return saturate((hdr * (A * hdr + B)) / (hdr * (C * hdr + D) + E));
}

이렇게 되어있는데 ACES는 표준이라는데 저 공식이면 끝나는건가?

https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/

언리얼 기본은 ACES 라네

https://docs.unrealengine.com/latest/INT/Support/Builds/ReleaseNotes/2015/4_8/

그리고 32x32x32 LUT로 50% 빨라졌다는데 뭔지;

머 대강 먼가 시스템이 있는데 근사하는 매핑 커브를 얻었다는거 같다.

머 그담에...

https://developer.nvidia.com/high-dynamic-range-display-development

900 및 1000 시리즈의 모든 NVIDIA GPU는 HDR 디스플레이 출력을 지원합니다

1\. 내부적으로 좋은 HDR 데이터를 렌더링합니다.
2\. 렌더링 된 색상 데이터의 정밀도 유지.
3\. 장면을 적절하게 매핑합니다.
4\. NVAPI를 사용하여 드라이버와 통신하고 HDR 이미지를 표시하고자 함을 표시하십시오.

NVAPI 를 통해 통신하라는데;

언리얼에서는 어떻게 박은거지?

https://docs.unrealengine.com/latest/INT/Engine/Rendering/HDRDisplayOutput/index.html

MiniEngine 에서는 Direct 12 에서 그냥 HDR 스왑체인을 생성하고 끝이네;

DXGI_FORMAT SwapChainFormats[2] = { DXGI_FORMAT_R10G10B10A2_UNORM, DXGI_FORMAT_R10G10B10A2_UNORM };

그 다음엔 자동 노출보정이다.

https://knarkowicz.wordpress.com/2016/01/09/automatic-exposure/

자동 노출에 대한 표준 접근 방식은 장면의 기하 평균 (log2 평균)을 계산하고,
그런 다음 모든 픽셀에 노출을 곱하고 톤 매핑, 색상 채점 및 감마를 추가합니다.

다른 곳에서도 늘 나오는 이런 저런 문제로 인해 평균은 때려치우고 결국 히스토그램이 등장했다는 건데

MiniEngine 에서는

  1. Extract Luma
  2. Generate Histogram
  3. AdaptExposure

로 구성되는데

  1. Luma

가로 세로 0.5 배 한 이미지에 Luma 를 1~255로 변경하여 기록한다.

// Use 4 bilinear samples to guarantee we don't undersample when downsizing by more than 2x
float3 color1 = SourceTex.SampleLevel( BiLinearClamp, uv + float2(-offset.x, -offset.y), 0 );
float3 color2 = SourceTex.SampleLevel( BiLinearClamp, uv + float2( offset.x, -offset.y), 0 );
float3 color3 = SourceTex.SampleLevel( BiLinearClamp, uv + float2(-offset.x,  offset.y), 0 );
float3 color4 = SourceTex.SampleLevel( BiLinearClamp, uv + float2( offset.x,  offset.y), 0 );

buffer manager 에서 무조건 1/4 이상을 유지합시다라고 했기에 BiLinear는 필요없을것 같은데 사람일은 모르니께 그냥 넘어가자

    const float MinLog = Exposure[4];
    const float RcpLogRange = Exposure[7];
    float logLuma = saturate((log2(luma) - MinLog) * RcpLogRange);    // Rescale to [0.0, 1.0]
    LumaResult[DTid.xy] = logLuma * 254.0 + 1.0;                    // Rescale to [1, 255]

Min은 -12, Max는 4이다.

  1. Histogram

384라는 값이 나와서 당황했는데

Context.SetPipelineState(GenerateHistogramCS);
Context.Dispatch2D(g_LumaLR.GetWidth(), g_LumaLR.GetHeight(), 16, 384);

별거 없어 보인다. 그냥 하나의 thread excution 에 단순히 InterlockedAdd 하기엔 아까웠는지 24칸을 수행한다는 것 같다.

16*24 = 384

// Loop 24 times until the entire column has been processed
for (uint TopY = 0; TopY < 384; TopY += 16)
{
    uint QuantizedLogLuma = LumaBuf[DTid.xy + uint2(0, TopY)];
    InterlockedAdd( g_TileHistogram[QuantizedLogLuma], 1 );
}
  1. Adaptive Exposer

먼가 코드가 길다?

마지막으로 히스토그램을 계산 한 후 가장 어두운 픽셀의 큰 비율
(50 % -80 %), 가장 밝은 픽셀의 작은 비율 (2 % -20 %)을 건너 뛰고 나
머지 픽셀의 평균을 계산합니다.
이 방법을 계량하면 자동 노출이 안정화되고 중요 무언가에 집중할 수 있습니다.

인데, 이건 머냐;

[numthreads( 256, 1, 1 )]

256 칸 밖에 안된느데 그냥 하면 안되나;;

// 0은 빈칸, 1-255 까지, 존재
float WeightedSum = (float)GI * (float)Histogram.Load(GI * 4);

[unroll]
for (uint i = 1; i < 256; i *= 2)
{
    gs_Accum[GI] = WeightedSum;                    // Write
    GroupMemoryBarrierWithGroupSync();            // Sync
    WeightedSum += gs_Accum[(GI + i) % 256];    // Read
    GroupMemoryBarrierWithGroupSync();            // Sync
}

그냥 for문 돌리면 히스토그램 생성 + 다시 저장까지 합쳐서 0.071 ms 인데, 위 방법으로 하면 0.067 ms 가 나온다. 진짜 대충 측정했기에 의미가 있는지는 모르겠지만 아주 약간은 도움이 된다는 걸로;

256칸의 합을 계산하기 위해, 0+1, 1+2, 2+3,... 이걸 다시 0,1,2 칸에 저장 0+2, 1+3, 2+4,... 이러면, 0+1+2+3 이됨. 이런식. 그다음엔 그냥 평균 구하고, 원복 시키고 등등

이걸 다하면,

먼가 보이다가 급속도록 검해지는 화면

cbuffer 값을 설정안해뒀네;

너무 어둡다.

float targetExposure = TargetLuminance / log(AvgLuminance);

Key 값을 받는구나;

시커먼 곳을 보다가 돌리면,

어두어진다.

'Game > MMD' 카테고리의 다른 글

Outline (2)  (0) 2017.10.06
Deferred Rendering (2)  (0) 2017.10.02
Outline (1)  (0) 2017.09.21
Deferred Rendering (1)  (0) 2017.09.19
Bullet (5) Skinning  (0) 2017.08.31
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크