【UE5で気分転換!セルルック×ライティングでキャラを魅せたい】第8回:珈琲時間 前編 – Unreal Engine 5.6

Unreal Engine

水ノ茉の宣伝

準備中...
ゲームを作る予定なの
水ノ茉こおり

シリーズ説明

本シリーズで最終的に検証したい内容は、DMXで制御されたバカみたいな量のライト、それらすべてをキャラクターに当てても破綻しないレンダリングパイプラインの構築です。

実装の手段は選ばないため、普段展開している【UE5】◯◯◯ – Unreal Engine ◯.◯と比べると多少敷居が高く、また、実装の再現性を提供しないこともあるため、いつも以上に趣味嗜好が全開となります。

始まり

絶賛、燃え尽き症候群、なのです。

やりたいことをやり過ぎて飽きたのです。

リターゲット作業

7月の3連休はほぼすべてをリターゲット作業に費やしました。死屍累々です。

LowerBodyとUpperBodyのボーン構成にかなり振り回されました。学生時代ぶりにMMDと触れ合って、そして地獄の作業ということを思い出しましたわ。何度枕に頭をうずめたことか。

苦痛の代償としてリターゲットのやり方をさらにマスターしました。とはいえ、このリターゲット作業、必要なタイミングが限定的過ぎて、その頃には忘れているのですよね。記録に残そうと思っても、適当にポチポチしてプレビューして違和感を消していく感覚的な作業だから言語化も難しい。

左の子が初版、右の子が最後の方に出力した子です。最後の方はLowerとUpperの特性を少しずつ理解し始めたので腰が正しく曲がるようになりました。愚直にやったら上半身と下半身で適用される回転が異なってどえらい見た目になって草生えた記憶です。ボーン構成が違うことも相まって、リターゲット単体だとどうしても細かい違和感が残ってしまうので、面倒ですがアニメーション出力してBlenderで微調整を予定です。本当に面倒だから気が向いたらやるとします。

ステージ召喚

UEのサンプルプロジェクトのDMXPrevisSampleを召喚です。

いつぞやにアーカイブして以来ですね。デレデレな鳥羽莉とばりさん可愛いですよね。あれです。白亜はくあさんと似たような可愛さです。

水ノ茉こおりのシナリオさんに配置指示をもらって、FBもらって、疑問点投げつけて、微調整して、ほぼ完成です。

多層マルチパスに合わせた陰影正規化の改修

現状はマルチパスの各パスごとに陰影の正規化を行っていますが、これだとパスごとに明暗結果が大きく変わる可能性をはらんでいるため不適切な状態です。

例えば前髪のパス不透明な衣装や身体のパスでは、描画されるメッシュ形状が完全一致していないため、当然なことにNoLの最小値と最大値の結果がパスごとに変わります。この結果を元に正規化をすると、パスによっては不自然に明るかったり暗かったりすることがあるのです。

前回は多層マルチパスが実装可能か、それを検証することが主軸だったので、この問題は無視して問題ありませんでした。実装可能なことが証明でき、実際に組み込まれた現在では、これは直さないといけない問題なのです。ということでパイプラインの改修をしていきます。

シンプルにすべてのパスのプレライトパスを実行させて、その後に集積、その後にメインライトパスという形に改修です。

集積パスでは有効なパスのバリエーションのみ立てておきます。

FToonShadowCalculationCS::FPermutationDomain PermutationVector;
PermutationVector.Set<FToonShadowCalculationCS::FToonPass1st>(ToonPassTypes[0]);
PermutationVector.Set<FToonShadowCalculationCS::FToonPass2nd>(ToonPassTypes[1]);
PermutationVector.Set<FToonShadowCalculationCS::FToonPass3rd>(ToonPassTypes[2]);
PermutationVector.Set<FToonShadowCalculationCS::FToonPass4th>(ToonPassTypes[3]);
PermutationVector.Set<FToonShadowCalculationCS::FToonPass5th>(ToonPassTypes[4]);

こんな感じで4thパスに投下されるメッシュがひとつもない場合は4thがないバリエーションで動作していることが分かります。

ライティングパスの最適化

先の通り現状は陰影とライト光量を書き込むためのプレパスと正規化後の陰影を元にライティングをするメインパスのふたつに分かれています。プレパスとメインパスに分かれているため、シャドウやライトの処理を2回していることになります。シンプルに負荷が2倍なのです。

メインパスでわざわざ再度、シャドウの計算をしているのは、スペキュラやリムを正規化されたライトカラーでライティングするためですが、普通に考えてプレパスで書き込んでおいて、その後に乗算パスを1枚挟むことで調整出来ます。ということでライティングパスの最適化をしていきます。

専用のシェーダーとパスを2つ作るだけで終わりです。

ブレンドモードの指定はラッピングされていても特に変わりありません。

// 乗算ブレンド
TStaticBlendState<CW_RGB, BO_Add, BF_DestColor, BF_Zero>::GetRHI()

// 加算ブレンド
TStaticBlendState<CW_RGB, BO_Add, BF_One, BF_One>::GetRHI()

シャドウカラーとGバッファの精度

シャドウだけじゃなくて値が極端に小さい場合や大きい場合に注意する点です。

左はBaseColor * 0.01をGバッファに格納、それを取り出してライティングした見た目です。右はBaseColorをGバッファに格納、それを取り出してから0.01を乗算してライティングした見た目です。

比べて見ると左は画像処理でいう量子化されたような見た目になっています。画面の明るさを上げたり、目を凝らさないと分からないですが。

この見た目になる理由は、Gバッファの精度によるものです。一般的なGバッファの精度である8ビットは256階調で、この場合の最小幅はrcp(255.0)約0.00392です。つまりこれより小さい幅を表現しようとしても強制的に丸め込みが起きて損失します。結果としてこのような量子化された見た目になってしまいます。回避方法は右のようにランタイムで計算したり、特定の値範囲に特化したエンコード、デコード処理を組むことです。

常識的なことですが、たまに失念することがあるのでアーカイブでした。

透明度を考慮した影

半透明な衣装の後景にある肌には、衣装の影、シャドウが落ちます。落ちること自体はシャドウ計算の仕様なのですが、半透明の物体が生成した影にしては暗すぎます。違和感がすごいので透明度を考慮できるように軽く改修します。

実装方法としてパッと思い付くのは3つくらいです。上から順に実装コスト&処理負荷が高いが綺麗な方法です。

  1. シャドウデプスを書き込む際に透明度を書き込む
  2. レイを飛ばして影を落としたオブジェクトの透明度を引っ張ってくる
  3. 自身の前面にあるパスのGバッファをスクリーンスペースで取ってくる

面倒なので一番簡単な3番の方法でお茶を濁します。前面に半透明なオブジェクトがあることは無事検出できました。

これを元に透明度を少し弄れば完成です。影の違和感が無事解消できました。

ルックが定まらない件

僕の考えた最強のパイプラインを生み出してしまったせいでNPRもPBRも顕現出来ます。

それが故に永遠と作りたいルックが定まりません。

今も尚困り続けています。

技術的な問題は解決策が存在するのですが、こういう感情的な部分は解決策が実質的に存在しないので困ります。

困ります。

おわり!!!

次回はシナリオさんから提出されるDMX制御の資料に依存するため未定です。

提出される前にルックの方向性を定めないといけないのですが、悩ましいのですよねぇ。個人的にはエロゲルックをしたいけど、あれは計算だけじゃなくて、テクスチャにも依存するような見た目なので、お絵かきが出来ないエンジニアだけでは求める品質を達成するのが難しいという現実があります。かといってアニメルックは散々やって飽きたから新鮮味がないんだよね。

取れる選択肢が多すぎるが故の悩みなのでした。

セルルック関連

えちえち GPU Cloth Simulation

CPU及びGPUクロスシミュレーションの続きです。

CoffeeLiveで達成したいことの番外編ですね。

PBDとXPBD

クロスを触ったことある方ならご存知でしょうが、PBDはイテレーション回数が見た目に大きく影響します。XPBDはそれの対策案として生み出された手法なのかな。厳密には見た目が変わるというより剛性な気ですが、分かりやすく見た目という表記です。

左がイテレーション3回、右が7回です。右の方が柔らかそうですよね。剛性以外のパラメータで柔らかさが変わるのは制御の観点で少々扱いづらいです。他にも処理負荷を分散させるアプローチとして安易に思い付くであろうイテレーションの動的調整も難しくなります。

XPBDだと見た目の柔らかさは変わらずに、皺がより綺麗に表現されるようになりました。微妙に布が伸びているのは初期値として設定していたコンプライアンス値が大きすぎただけなので気にせず。変数を少し追加しただけでここまで変わるんですね。物理の世界はすげぇですわ。意味分からんもん。

Static & SkeletalMesh対応の準備

最初から純正のメッシュコンポーネントに組み込もうとすると作業イテレーションが最悪なので、自前のメッシュ描画コンポーネントに頂点情報を詰めて疑似的に再現しながら作業します。

まずはProceduralと似たような単純なメッシュを元に実験です。

思い付きの制約構築方法が素直に上手くいってハッピーです。

Proceduralと同様の挙動ですね。

非グリッドメッシュはいくつか躓きポイントがありましたが、無事に成功です。スカートを揺らすとよりクロスシミュ味を感じますね。

衝突テスト

簡易的な衝突テストです。形状の内外判定やら距離は顔陰作成ツールでライブラリ化していたので簡単です。自前で遊んでいると過去の成果物が役立つことが多いですよね。

セルフコリジョン

自身の生地で貫通しないようにセルフコリジョンを簡易的に導入しようと試しましたが難しいですね。面倒がらずに空間ハッシュとかの正式な実装方法を試した方が手っ取り早そうな雰囲気です。

5.6.1アプデ

実装把握も兼ねて守備範囲は手動でマージをしているのですが、流石に多いですね。

普段より体感多いなと感じましたが、思えばCoffeeLiveでは使用しない機能を片っ端から削除した所為でそれらがWinMergeの差分ファイルに乗っかっていました。やっぱりコード丸ごと削除は保守性の観点から避けるべきでしたね。いつもの#if ~ #endifが面倒ですが一番ラクなことが分かりました。

都度パッチを作成して適用すればこんなことをしなくても済みますが、流石に面倒なんですよね。

コミットログは毎週暇つぶしに読んでいるので緊急性高いものは割とすぐに取り込みますが、それ以外のチマチマしたものってどうしても後回しにしちゃいます。