トーンマップいろいろ
はじめに
今回はトーンマップをいろいろ作って遊んでみます。
前回の続きです。
前提として,
- 平均輝度での露光処理
- ガンマ補正
- ブルーム処理
などを行っています
トーンマップ
線形マップ
ただ入力をそのまま出力しただけです。もはやトーンマップではないです。
一応ガンマ補正やらブルーム処理の結果のリファレンスとして。
Reinhard
前回も登場したReinhard関数。
http://www.cs.utah.edu/~reinhard/cdrom/
前との違いは,白色点(1,1,1)にマップする輝度を指定するような形になっているところ。
のときが前回と同じ関数です。のときの関数系と,線形トーンマップを合成したような形となっています。
exp
1.0でサチるような関数を考えた結果,信頼と実績の指数関数を使ってみました。
これも,白色点にマップする輝度を考えて設計しています。
8bitのSDRテクスチャにマッピングすると考えて,輝度が254/255以上となるような値を白色と定義しています。
log
指数関数があるなら,対数関数でトーンマップしてもいいだろうと作ってみたやつです。
ACES Filmic Tonemapping Curve
https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/
フィルム調なトーンマップです。
Ucharted2で使われていたらしいACESトーンマップを使いやすくしたための近似らしいです。
指数関数とかReinhardと違って,上に凸というわけではなく,輝度の小さい領域では立ち下がっていて,
暗い部分はより暗く,明るい部分はより明るくといったような,微妙にS字型となっているトーンマップです。
RGB or 輝度
今まで例に出していたトーンマップの適用結果の画像は,RGBの各チャンネル毎にトーンマップ関数を適用するRGBベースのものです。 ですが,少しだけ修正してあげれば輝度ベースで適用することも可能です。
// トーンマップの適用関数 // @param color 入力値 // @param exposure 露光値 // @param whiteLuminance 白色(1,1,1)にマッピングされる輝度 float3 Tonemap(int funcType, float3 color, float exposure, float whiteLuminance) { #ifdef LUMBASE // 輝度ベースのトーンマップのとき const float luminance = GetLuminance(color); return color / luminance * TonemapFunction(funcType, luminance*exposure, whiteLuminance); #else // RGBベースのトーンマップのとき return TonemapFunction(funcType, color*exposure, whiteLuminance); #endif }
輝度に対して,トーンマップをかけると各チャンネル間の差を変えることなく彩度を保ってマッピングができます。
Reinhard
RGB |
Luminance |
---|
ACES
RGB |
Luminance |
---|
異なる露光の合成
いわゆるHDR写真みたいな,異なる露光でトーンマッピングを行って 白飛びした領域のみ,低い露光値を適用するトーンマッピングを試しにやってみたいと思います。
まずは,ACESをつかって,露光の高い白飛びしたシーンを用意。
このシーンの0.8以上の白飛びしかけのピクセルをピンク色で図示するとこんな感じ。
で、白飛びしかけのシーンを露光1/4倍で暗くマッピングしたものと補間しながら合成。
const float3 mappedColor1 = Tonemap(TONEMAP_FUNC_TYPE, outputColor.rgb, EXPOSURE, WHITE_LUMINANCE); const float3 mappedColor2 = Tonemap(TONEMAP_FUNC_TYPE, outputColor.rgb, EXPOSURE / 4.0f, WHITE_LUMINANCE); #ifdef LUMBASE const float lum = GetLuminance(mappedColor1); #else const float lum = max(mappedColor1.r, max(mappedColor1.g, mappedColor1.b)); #endif const float x = smoothstep(0.8f, 1.0f, lum); outputColor.rgb = lerp(mappedColor1.rgb, mappedColor2.rgb, x);
左が合成前,右が合成後
確かに白飛びしているウサギの顔や,空の青色がはっきり見える。
ただ,不自然に明暗が変わっていて微妙な気もします。調整の余地あり。