【UE5】ベタ塗り・トゥーン表現と相性が微妙なトーンマッパ(Tonemapper)と自動露出(Auto Exposure)について

始まり

これはUnlitとポストプロセスの設定を軽く調整して作成した絵です。
※キャラクターがUnlit、背景はDefaultLitなど


ポストプロセスの設定を初期値にするとこのような絵になります。


比べるとポストプロセスの設定が初期値の方はキャラクターの色が明るいというか薄いですね。

初期値 調整後


何故でしょうか。
今日は、そんなお話。

原因を探していく

原因になり得るところから順に調べていきましょうか。

Unlitシェーダー

Unlitシェーダーは他のゲームエンジンと同様にフォワード・ディファードライティングに関係なくメッシュ描画時にSceneColorに書き込んで完結するシェーダーです。

Unreal EngineもUnlitで書いたピクセルはLumenの対象外であったり、環境光・反射光が反映されなかったりと、Unlitらしい挙動です。※LumenはUnlit自体がGIを発生させないだけで周辺のGIの影響は受けます。

RenderDocで見るとUnlitで書かれたピクセルはBasePassとその後のLightsPassでも変化がないことがわかります。

左がBasePass 右がLightsPass

色が薄くなっている原因にUnlitシェーダーは関わってなさそうですね。

『そりゃそうだろ。』と思うかもしれませんがUnreal Engineレンダリングに独自の仕様というか俺様ルールを設けていたりして、微妙に思ってたんと違うことがあるので、基本的には疑って見た方が解決までの道のりが近くなる傾向があります。

ポストエフェクト(ポスト効果)

まぁ原因はココですよね。
ポストプロセスの設定を"初期値"と書いているんですから。

ポストエフェクトにはブルームや被写界深度、半透明合成など後でやるものが全て詰め込まれています。

ポストプロセスマテリアル > 半透明合成 > 被写界深度 > ブルーム > トーンマップ > スクリーンスペースアンチエイリアス


その中のどのパスが原因なのかを絞っていくとTonemapPassに辿り着きます。

左がBeforeTonemapPass 右がAfterTonemapPass


TonemapPassはBloomPassのCompositeやトーンマッパ、自動露出の適用、各種カラーグレーディングの適用など、色まわりの最終調整パスです。そんな感じで要素が多すぎるので結論だけ書くとトーンマッパと自動露出が原因なのです。

厳密には有効/無効で括れる話ではないですが、トーンマッパと自動露出の比較表を見ると、両方無効の場合はUnlitと同様の入出力を確認できます。

トーンマッパ (有) トーンマッパ (無) Unlit
自動露出 (有)
自動露出 (無)
Unlit

結局は好みの問題

先の比較表を参考にすると入出力の色を担保するためには、自動露出とトーンマッパを無効にする必要があります。然しながらトーンマッパを無効にした場合は発光が強いを通り越してきついです。見る人によってはオーバーフローしてねと思われてしまうほど。実際それに近いんですけど。

そんな理由から筆者は自動露出は無効、トーンマッパは有効、Unlitシェーダー側にガンマと輝度調整のノードを挿入で対応しています。筆者が目指す絵柄がブルームによる光の放射というか拡散感が欲しいので、トーンマッパを無効にしちゃうと結構調整が厳しくてこの感じに落ち着きました。

自動露出無効、トーンマッパ有効、色補正無効
自動露出無効、トーンマッパ有効、色補正有効

ちなみに処理とメモリの負荷を極度に気にしない動作環境を用意できるのであればTonemapLUTを2種類作成するのも手です。というのも5.3からはTonemapLUTがパラメータ変更された場合のみ作成されるように最適化されたので、以前のように毎フレームTonemapLUT作成パスが実行されなくなり、常駐の負荷がごっそり削れました。地味に3Dテクスチャなので作成・計算コストが重かったんですよね。キャッシュ部分を自前で改造する必要が無くなったのは有難い。

自動露出(Auto Exposure)の設定例


  • Exposure > Metering Mode > Manual
  • Exposure > Exposure Compensation > 0.0
  • Exposure > Apply Physical Camera Exposure > False

トーンマッパ(Tonemapper)の設定例


  • Misc > Expand Gamut > 0.0
  • Misc > Tone Curve Amount > 0.0

色補正


WorkingColor = PositiveClampedPow(WorkingColor, rcp(ColorGamma));
WorkingColor = WorkingColor * ColorGain;
return WorkingColor;

PositiveClampedPowWorkingColorが0.0以下の場合に0.0を返す3項演算子なpow関数、rcpは1.0除算を、精度を犠牲に計算速度が速いアレです。

シェーダ vs シェーダー

長音符を付けたり付けなかったり、筆者的には伝わればどちらでも良い派。

そんな疑問を会社で投げ飛ばしたら先輩社員さんに「昔はメモリサイズ云々」という記事を共有してもらえました。

はぇぇぇ。4文字 vs 5文字かと、納得。

技術的な背景があるとすんなり納得できますよね。

そんなことをふと思い出したの。

おわり!!!

か わ い い

か っ こ い い

初音島ではこういう景色が広がっているんでしょうか。
想像するだけで幸せです。

このビジュアルを見せたいという自己顕示欲に溺れて適当にネタを結び付けて爆速で書いた記事なので内容が薄いです。