2018年11月24日土曜日

UE4での線形出力とフィルミックトーンマップ

ue4のことは詳しくないが今後はUE4でライティングの検証を色々していきたいと考えているので、
その際にトーンカーブや自動露出が邪魔なのでそれらを無効にしたくて色々と調査したのでその時のメモを残しておく。
実はもっと効率的な方法とかはあるのかもしれない。
使用したUEのバージョンは4.21.0

エミッシブの値は輝度そのものと考えてよさそう。UE4の内部では1.0の値を1.0[cd/㎡]として扱っていると考えられる。
ただし、レンダリングされる値はカメラの露出設定やトーンカーブによって変わる

先ずはエミッシブ1.0(輝度1.0[cd/㎡])を1.0でレンダリングさせたい。
その後に露出制御やトーンマップについて調べていく。

(放射輝度と輝度は違うものだが、今回は輝度と言ってしまってもあまり問題にならないので気にしないで進める)

検証方法

用意するマテリアル

unlitのエミッシブを適当な数値にして並べた物がどう表示されるかを観察する


8bitテクスチャだと0.01など切りの良い数値が用意しづらいうえに、圧縮の影響もうけそうなのでコンスタント値にした。
(openEXRならテクスチャでも大丈夫だと思う)

表示される理論値

入力がそのまま出力された場合は以下のようになるはず。

「ガンマ2.2の補正」の場合の理論値

「sRGBのガンマ補正」の場合の理論値

実際の表示はこうなっている。何もせずににエディタで表示するとこんな感じにる
これは自動露出やトーンカーブの影響が大きい。

これを理論値としてレンダリングさせるためにはカメラの露出値について知る必要がある。

EV(exposure value)とは

LV(light value)というものがあり、これはシーンにどれ位の強さのライトが当たっているかを示すもの。
この数値が1増えると2倍の明るさを意味し。1減ると半分の明るさを意味する。

EVはLVと似たものでカメラに入る光をどれだけ弱めるかを意味するもの。
LVとは意味合いが逆に感じるかもしれないが、1増えると取り込む光を半分にし、1減ると取り込む光を倍にするということ。
なのでLVと同じ数値のEVで撮影すると適正露出ということになる。(センサに当たる光量が最適で且つ常に一定ということ)

現実のカメラでは被写体にあたっている光の強さ(LV)はわからない。
カメラ内蔵の露出計は反射された輝度を見ている(反射式露出計)ので適正露出ではないことが多い。
カメラは素材が白くて明るいのか光が強くて明るいのかの区別がつかないため中間グレーの素材に限って適正露出となる。
カメラからしてみると間をとって全てのものが中間グレーと決めつけている。

明るめに撮影したい場合は1段明るくする(プラス補正)等というが、これは今のEV値から1引いた状態にするということ。
暗めに撮影したい場合は1段暗くする(マイナス補正)等というが、今のEVに1を足した状態にするということ。

実際にはカメラの画面の場所毎にライトバリューは違うので、その場所の正確なライトバリューを知りたい場合は照度計や入射型露出計で測ることになる。(カメラマンが被写体に当たる光を計測している光景を見たことがあると思うけどそれのこと)

カメラで取り込む光をコントロールするものにシャッター速度と絞りがあるが、
これらによってどれだけ光が増減されるかを示すのがTV(time value)とAV(aperture value)と呼ばれるもの。

EVはTVとAVの和できまる。
EV = TV + AV

TVはシャッタ-速度で決まる露出値だがシャッター速度ではないので注意。
AVは絞りで決まる露出値だが、絞りではないので注意。
TVはシャッター速度が倍々に長くなっていくと1ずつ下がっていく。
TV = -log2(シャッター速度)

AVは絞りの面積が倍々に増えると1ずつ下がっていく。
絞りは有効口径というものが関係していて、口径が半分になると面積は1/4になり光の量は1/4になる。(これは円の半径と面積の関係)
TV = log2(絞り^2)

例えばシャッター速度1/60[s]、 絞りF8だった時EVは以下となる。(googleの検索バーにペーストすると答えが出る)
-log2(1/60) + log2(8^2))
EV値は約12となる

EV値についてはここがわかり易い
小学生でも分かるEV値の話(EV値とAV値とTV値の関係)
ライトバリューとエクスポージャーバリュー | tokyo-photo.net

EVと照度と輝度の関係

EV値0は一般的にLV0(2.5[lx])で照らされたものを撮影する露光量と言われる。(ただしそこまで厳密なものではないしカメラの機種ごとに差がある)

diffuse18%のグレーカードがあったとして2.5[lx]で照らされた場合の輝度は
2.5 * 0.18 /π = 0.143[cd/m2]
diffuse100%の白の場合は
2.5 * 1.00 /π = 0.796[cd/m2]

diffuse100%の輝度が1.0[cd/m2]になるための照度は3.14[lx]である。
3.14[lx]照らされたものをEV0で撮影すると少し明るく写るということがわかると思う。

このEV0をリニアセンサーでそのまま撮影されたとすると、
0.796[cd/m2]が1.0で表現され、1.0[cd/m2]は1.257で表現されるためこれを1.0で表現するためにはEV値は0.33とすればよいことになる。

ただしUEの場合0EVでエミッシブ1.0が0.833(γ2.2の8bitで235)でレンダリングされている。
1.2[cd/m2]が1.0で表現され、1.0[cd/m2]は0.833で表現されるためこれを1.0で表現するためにはEV値は-0.236とすればよい。

UEでは2.5[lx]基準より1.5倍明るい部分まで記録でき、2/3の明るさでレンダリングされると考えられる。

0EVの2.5[lx]基準についてはフィルムのころからの考えでもあり、デジカメのリニアセンサーではハイライトがいきなりクランプされてしまうため同じように適用するのは無理があるのかもしれない。
デジカメなどのリニアなセンサーではなくフィルムで考えると、フィルムの場合は明るい部分はかなり粘り、種類によっても特性が違うため、中間部分がどの輝度のものを記録されるかを考えたほうが良い。

実際のカメラのrawデータを素の状態で現像すると1段位暗いと感じる。
リニアセンサーなのでハイライトを多少残すために暗く撮影し、中間を持ち上げるといったことをするためだと思う。

線形で確認するための方法

新規レベルの直後は注意。
新規レベルの直後は露出制御が機能していない。一度レベルを保存して開きなおすと正しい挙動になることを確認した。

ポスト処理を無効にする

ビューポートの表示メニューのpost Processingをすべて無効にする

カメラ露出を設定する

以下3種類の方法で確認できた。
ポストプロセスが全て無効になっていることが前提。

方法1 : exposureのGame Setthingsを有効にする

Eye Adaptationが無効なことが条件。
ビューポートのライティングメニューでexposureの設定をこのようにする(デフォルトでこうなっている)


ポスト処理を無効にした状態で、且つGameSettingsが有効だと輝度1.0が1.0でレンダリングされることを確認した。
チェックボックスが有効だとGameSettingsから露光が与えられるようだが、Eye Adaptationが無効だからなのかこのままで「エミッシブ値(輝度)」=「レンダリング結果」となる。

結果は理論値と同じになった(ただしsRGBガンマではなくガンマ2.2の方)

方法2 : カメラの露出を任意で固定にする

GameSettingsを無効にすることで任意の露出値を指定することができ、輝度1.0が1.0でレンダリングされるにはEV値-0.263を入れる。

理論値と同じ結果となった(2.2のガンマ補正の理論値)

なぜEV値を-0.263にするのか

パラメータにマウスカーソルを合わせると出てくるように以下の計算で求まった係数が掛けられるらしい
係数 = 1/(1.2*2^EV値)
デフォルトのEV1の場合だと係数は 1/2.4= 0.416..となり、半分よりも暗くなる。
今回は係数を1.0にしたいのでEV値を-0.263にすればよい。

方法3 : カメラの露出を任意で固定にするためにシャッター速度と絞りを決める

レンズの絞りとシャッター速度からでもEVは決まる。
EV0となるシャッターと絞りの組み合わせはいくらでもあるがわかり易いのは、
シャッタースピード1[s]、絞りF1.0、ISO感度100。

ただし、今回は輝度1.0を1.0でレンダリングさせたいのでUEではEV-0.263にしたい。
これはEV0と比べて1.2倍の明るく撮影するという意味なので、シャッター速度を1.2倍長くするか、絞りをF0.912にするか、ISO感度を120にすればよい。

1.2倍明るくレンダリングさせるために今回は以下のように設定した




理論値と同じ結果となった(2.2のガンマ補正の理論値)

3種類の方法のまとめ

線形且つ輝度1を1.0で表示する簡単な設定は以下の3種類あることが分かった。

ポスト処理 カメラ露出
方法1 全て無効 Game Settings 有効
方法2 全て無効 Game Settings 無効。EV値を -0.263に設定する
方法3 Eye Adaptation 以外を無効 Game Settings 有効。レンズ設定のEVを0秒、f1、ISO120にしてEV値 -0.263を再現

Filmic Tone Mapperについて

フィルミックカーブ有効になっているとパラメータを駆使しても線形にできない。
また、フィルミックカーブを適用したものがどのような値になるのかを調べたのでその時のこともメモしておく。

UEのフィルミックカーブはACESのトーンマップが基準になっていて、これをさらに暗部、中間部、明部で調節しやすくしたものだと思われる。
フィルミックトーンマップのグラフはマニュアルに記載されているdosmosのリンクページに行けば確認できる。

デフォルトのフィルミックカーブのグラフ

横軸だけが対数になっているので注意。
分かりづらいのでリニア特性のカーブを黒で重ねた。

暗部が暗くされ、ハイライトが圧縮されていることがわかる。

入力-2.0とは実際の値では0.01のことで、-1は0.1、0は1、1は10のこと。
実際の入力値0.01が0.002にされ、0.1が0.086にされ、1.0が0.743にされ、10が1.003にされることがグラフからわかる。

もちろんこの後にガンマ補正が掛けられて表示される。
この時のガンマ補正はどうやら2.2のガンマではなくsRGBガンマ。

デフォルトのカーブは暗部つぶしすぎているのでデフォを使うことは少なさそう。

縦軸も対数グラフ

横軸が対数なのでわかりづらいため、縦軸も対数にしてみると暗部のコントラストがついていることがよくわかる。
横軸が-1位(実際の入力は0.1位)よりも低い部分が暗部と思ってよい。
カーブの傾きがリニアと比べて強いのでコントラストが強いということ。
一方で横軸が-0.5位(実際の入力は0.32位)の中間部から1.0近く(実際の値は10)まで傾きが緩やかになっていて、これはハイライトが圧縮され急な白飛びが防がれるがコントラストが下がっているということでもある

線形グラフ

両軸とも線形のグラフのほうが見慣れていると思うのでその場合は一番上の式を以下のように書き換えればよい

こちらの方が見慣れたグラフだと思う

線形に近づけるパラメータはこんな感じが限界

フィルミックカーブの実際の値

少し暗く出ていることが分かった。
トーンカーブを掛ける前に0.922倍程度掛けられている。(理由はわからない)
カーブ適用後はsRGBのガンマ補正が適用されている。

フィルミックカーブを検証した方法

デフォルトのフィルミックカーブを使い、本当に同じ値が出力されているかを以下の値を使って確認する


上で行った方法1~3と同じ様な方法だがポスト処理部分のTonemapperを有効にするだけ。

ポスト処理カメラ露出
方法1Tonemapperのみ有効Game Settings 有効
方法2Tonemapperのみ有効Game Settings 無効。EV値を -0.263に設定する
方法3TonemapperとEye Adaptationのみ有効Game Settings 有効。レンズ設定のEVを0秒、f1、ISO120にしてEV値 -0.263を再現

デフォルトのフィルミックカーブを適用すると以下のようになるはず
入力 0 0.01 0.02 0.04 0.08 0.16 0.32 0.64 1 2 4 8
出力 0 0.00191 0.00616 0.019681 0.06089 0.17157 0.3884 0.624 0.7433 0.874 0.9512 0.9937
γ2.2(8bit) 0 15 25 43 72 114166 206 223 240 249 254
sRGB(8bit) 0 6 18 38 70 115 167 207 224 240 249 254

なのでこのような見え方になるはず(sRGBガンマ)

でも実際はこのようになっている。
上の数値はsRGBガンマの値だがリニアの数値で比較したら一定で暗くなっていることが分かった。

カメラのEV値を少し調整したら一致した。
本来ならUEの場合はEV-0.263を入れるがEV-0.38で一致することが分かった。
少し明るく(1.0845倍位)撮影することで理論値と一致した。

なのでトーンマップ計算式のカーブの入力値を0.922倍したところ実際の値と一致した
一番上の計算式を以下のように変える

この条件で計算式をプロットしたものがこれ
入力00.010.020.040.080.160.320.641248
出力00.001660.005370.017190.053510.153220.3580.59920.72370.86180.94430.99
γ2.2(8bit)014244067109160202220238248254
sRGB(8bit)05163565109161203221239249254
ここのsRGBの値と実際の値が一致した

HDRイメージでトーンマップを比較

以前用意した正しい明るさで現像したHDR写真があるので、これを使って比較する
http://technorgb.blogspot.com/2017/12/hdr.html
ついでにカメラの露出値もUE側で設定し、現実のEV値と似た感じになるかも確かめる

この写真は100[cd/㎡]を1.0としているため、UEの明るさと合わせるために100倍している

比較する内容
  • リファレンス :
    線形な画像としてのリファレンス。
    HDR画像をUEの露出値に合わせてフォトショップで露出調整したものをキャプチャしたもの。UEとの違いであるEV0.263を考慮している(ディスプレイガンマはsRGB)
  • トーンカーブ無し :
    UEでの線形な画像。
    ただしディスプレイガンマ2.2なのでsRGBと比べると暗部が少し明るくなる。
  • フィルミックトーンカーブデフォルト :
    UEのデフォルトのフィルミックカーブ。
    暗部が暗くなり、ハイライトが粘る。
  • 線形に近づけたフィルミックカーブ :
    UEのフィルミックカーブをパラメータでできるだけ線形に近づけたもの。露光調整はUEのカメラ。暗部はリファレンスと似ているがハイライトは少し粘る

例1

リファレンス(EV値 12.563+0.263)

UE4での露出設定 EV値 12.563

トーンカーブ無し

フィルミックトーンカーブ_デフォルト


線形に近づけたフィルミックカーブ


例2

リファレンス(EV値 14.621+0.263)

UE4での露出設定 EV値 14.621

トーンカーブ無し

フィルミックトーンカーブ_デフォルト

線形に近づけたフィルミックカーブ

例3

リファレンス(EV値 10.563+0.263)

UE4での露出設定 EV値 10.563

トーンカーブ無し


フィルミックトーンカーブ_デフォルト

線形に近づけたフィルミックカーブ

例4

リファレンス(EV値 7.293+0.263)

UE4での露出設定 EV値 7.293

トーンカーブ無し

フィルミックトーンカーブ_デフォルト

線形に近づけたフィルミックカーブ

例5

リファレンス(EV値 3.281+0.263)

UE4での露出設定 EV値 3.281

トーンカーブ無し

フィルミックトーンカーブ_デフォルト

線形に近づけたフィルミックカーブ