17.8K Views
November 27, 23
スライド概要
■概要
[StrandRendering]
アンチエイリアシングされた髪の毛のレンダリング方法について紹介をします。
[StrandLighting]
髪のリアルな彩度や奥行き・ボリューム感を得るために重要な効果であるマルチスキャッタリングの概要や実装手法、最適化などについてご紹介いたします。
※CAPCOM Open Conference Professional RE:2023 で公開された動画を一部改変してスライド化しております。
■想定スキル
髪の毛の表現周りの知識を持っている、もしくは実装経験がある方
詳細は下記公式サイトをご確認ください。
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
CAPCOM Open Conference Professional RE:2023
https://www.capcom-games.com/coc/2023/
カプコンR&Dの最新情報は公式Twitterをチェック!
https://twitter.com/capcom_randd
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
株式会社カプコンが誇るゲームエンジン「RE ENGINE」を開発している技術研究統括によるカプコン公式アカウントです。 これまでの技術カンファレンスなどで行った講演資料を公開しています。 【CAPCOM オープンカンファレンス プロフェッショナル RE:2023】 https://www.capcom-games.com/coc/2023/ 【CAPCOM オープンカンファレンス RE:2022】 https://www.capcom.co.jp/RE2022/ 【CAPCOM オープンカンファレンス RE:2019】 http://www.capcom.co.jp/RE2019/
BIOHAZARD RE:4の髪の毛解説 BIOHAZARD RE:4のStrand表現について発表します。 本講演は2部構成となっています。 ©CAPCOM 1
Strand Rendering 皆さんこんにちは Strand Renderingと題して発表させていただきます。 2 ©CAPCOM 2
髪の毛の歴史 半透明の髪の毛時代 • PS3 / XBOX360 のバイオハザード6 • Kajiya + Forward • 不透明部分をZPrePassし半透明で描画 シェーダーグラフ時代 • バイオハザード7 … present • 自由な素材設定 • DeferredなのでDitheringで半透明感を作成 • 髪の毛感は既存のパラメータで頑張る 髪の毛の品質改善が望まれる • TAAのアンチエイリアシングの限界 • 照明を正しくハンドリングできない etc. 髪の毛の歴史とても長いのですが、私が担当していた範囲でレオンをベースにお話しをします。 PS3/XBOX360時代は、半透明のフォーワードレンダリングを利用していました。 この時は不透明部分をZPrePassすることで高速化をしています。 また、最適化のために照明の数で異なるシェーダに切り替えるなどを行っていました。 3 次は、PS4/XBOX One時代です。 バイオハザード7から現在まで、シェーダグラフを使いアーティストが自由にシェーダを使えるようになりました。 また照明もリッチになった反面、解像度の増大と合わさり半透明での描画が困難となりました。 髪の毛はDeferredで描画し、ZPrePassを使いながら、Ditheringで表現しています。 ですが、多方面から髪の毛の品質改善が望まれるようになりました。 アートパワーによる見た目もとてもリッチです。 右のバイオハザード RE:2とバイオハザード RE:4(以降RE4)は、どちらもDeferredですがとてもいい感じです。 ですが、TAAによるアンチエイリアシングの品質や、照明を正しく表現できない問題がありました。 ©CAPCOM 3
毛一本一本時代の到来 PS5でとても印象的なサッカーゲームが登場 • レイトレーシングも需要だが、キャラクターの品質改善も重要! 高品質な髪の毛に求められるもの • アンチエイリアシングが効いた髪 • 説得力のあるライティング • ダイナミックなシミュレーション さて、ある時とても有名なサッカーゲームがPS5でいい感じの髪の毛を表現しているのを目の当たりにしました。 RE ENGINEのレンダリングユニットは、PS5世代ではレイトレーシングによる見た目の改善を主にやっていましたが、 4 キャラクターの品質改善も重要であると目覚めました。 特に、DigitalDragon2020で発表された、EAさんのPhysics and rendering behind frostbite’s hairは、 詳細な情報も我々のstrandの開発にとても役立ちました。 というわけで、高品質な髪の毛が求められます。 今回私は、アンチエイリアシングが効いた髪の毛の話をします。 ©CAPCOM 4
最終結果 出来上がりはこんな感じです。 上の画像はゲームランタイムで撮影したものです。 5 髪の毛は、Alembic経由で読み込んでいます。 RE4ではOrnatrixを使い毛を作成しています。 ©CAPCOM 5
全体の流れ Strandの描画で以下のループを実行 髪の毛の分類 とカリング ライティング ハードウェア ソフトウェア 描画 半透明描画 全体の流れを見ましょう。 Strandは毛の塊単位で動作します。 毛のレンダリングは、分類とカリングを初めに行い、ライティングをしてから、ハードウェア描画と、 ソフトウェアによる半透明描画を行っています。 ©CAPCOM 6 6
毛の分類とカリング 髪の毛の幅の投影面積で毛を分類 • 大きな線分はハードウェア描画 • 細く透明感が必要な髪の毛はソフトウェア描画 • マゼンタはハードウェアで描画、グリーンはソフトウェアで描画 1080p 4k 8k さて、毛の分類です。 髪の毛の幅の投影面積で毛を分類します。 画像を注目していただけますと解像度で、緑から紫に色が変わります。 7 解像度が低い場合は、ソフトウェアで描画し、解像度が高いとハードウェアで描画を行うため紫色になります。 詳細な説明は後半のソフトウェア半透明描画で行います。 ©CAPCOM 7
FHD ソフトウェア描画が支配的 FHD Software Rasterが支配的です。 8 ©CAPCOM 8
4K ハードウェア描画が増えてきた 4KだとHardwareRasterがそれなりに増えてきます。 9 ©CAPCOM 9
8K ほとんどハードウェア描画 8KならほとんどHardwareRasterに見えます 10 ©CAPCOM 10
ライティング Strand-based Hair Rendering in Frostbite[19] を参考に実装 • TTのAzimuthal RoughnessのLUTは独自に近似 照明結果は頂点カラーとして格納 • ラスタライズと照明を分離 • 半透明にも適応しやすい 結果 • 図は直接照明のSingle Scatteringの結果 • 正しいかはわからないけど見た目はそれっぽい • 黒髪以外は多重散乱が必要 • Strand Lightingにて詳細を説明 Single Scattering 次にライティングです。 ライティングは、2019年に発表されたStrand-based Hair Rendering in Frostbiteを参考にします。 11 照明結果は、頂点カラーとして描画の前に作成をしています。 事前作成することにより、ハードウェア・ソフトウェアどちらでの描画で利用することが可能です。 右側は直接照明のSingle Scatteringの結果です。 正しいかは正直わかりませんが、見た目はそれっぽいです。 黒い髪の毛以外は、多重散乱を計算する必要があります。 実際のゲームでは、多重散乱を利用しています。 詳細はStrand Lightingにて説明されます。 ©CAPCOM 11
ハードウェア描画 ソフトウェアラスタライザで大きな面積は負荷が高くて導入 カメラ方向を見るポリゴンとして描画 • 髪の毛の一本単位の完璧なモーションベロシティを計算 • AntialiasingはTAA頼み 次にハードウェアの描画です。 ここは特別な最適化はしておりません。 もともとすべてをソフトウェアラスタライザで実行するつもりでした。 12 幅をもった線を描画する場合は負担が大きいためハードウェアの対応が入りました。 髪の毛自体は、下の図のようにカメラ方向をみるビルボードのような形状で描画をしています。 この時、髪の毛一本単位で正しいモーションベロシティを計算しています。 これは、AntialiasingはTAAに頼ることを意味しています。 ©CAPCOM 12
低解像度は線が破綻しやすい こうしてハードウェアのラスタライザーで描画はできます。 大体正しい見た目ですが、線がところどころ飛んでいたり欠けたりしています。 ©CAPCOM 13 13
ソフトウェア描画と合成をした結果 これに先ほどのソフトウェア描画と合成するといい見た目になります。 14 ©CAPCOM 14
ソフトウェア描画と合成をした結果 拡大して比較をしてみましょう。 左はハードウェア+TAAです。 右はソフトウェアラスタライザーです。 15 ©CAPCOM 15
ソフトウェア描画の準備 初めに分類とカリングに相当する部分 4つのDispatchで描画準備を行う 1st 深度作成 2nd Froxelに線分を登録するメモリサイズの算出 線分のオクルージョンカリング用の 縮小深度バッファ ( 縦横それぞれ1 / 16 ) を作成 ・毛をラスタライズし、Froxelに書き込み毛の ・インデックスの保存に必要なサイズを計算 カウントは単純にInterlocked Addで実現 3rd Froxelに線分のメモリを割り当てる 4th Froxelに線分のindexを割り当てる ・Froxelに格納されたサイズからデータ書き出し先の 線形バッファのオフセットアドレスを確保 ・Froxelにオフセットアドレスを書き込み、 Froxelのカウンタを0リセット 再度毛の線分をラスタライズしながら オフセットアドレスとカウントを行い、 線分を登録 上記の処理はどのIHVでもスケールして動作 ソフトウェア描画の準備ですが、全体で4つのパスで構成されます。 一つ目は、髪の毛のオクルージョンカリング用の深度バッファを縦横 16分の1 にして作成します。 16 二つ目は、Froxelに髪の毛の線分を登録する際の各Voxelの容量の算出です。 ここでは髪の毛をラスタライズし、FroxelにInterlocked Addを使いカウンターをアップします。 三つ目は、メモリの確保です。 Froxelに対して結果を保存するための線形バッファのオフセットアドレスを確保します。 これはFroxelのカウントアップした容量を線形バッファのカウンターに対してInterlockedすることで確定できます。 この時ついでに容量をリセットします。 ©CAPCOM 16
ソフトウェア描画の準備 初めに分類とカリングに相当する部分 4つのDispatchで描画準備を行う 1st 深度作成 2nd Froxelに線分を登録するメモリサイズの算出 線分のオクルージョンカリング用の 縮小深度バッファ ( 縦横それぞれ1 / 16 ) を作成 ・毛をラスタライズし、Froxelに書き込み毛の ・インデックスの保存に必要なサイズを計算 カウントは単純にInterlocked Addで実現 3rd Froxelに線分のメモリを割り当てる 4th Froxelに線分のindexを割り当てる ・Froxelに格納されたサイズからデータ書き出し先の 線形バッファのオフセットアドレスを確保 ・Froxelにオフセットアドレスを書き込み、 Froxelのカウンタを0リセット 再度毛の線分をラスタライズしながら オフセットアドレスとカウントを行い、 線分を登録 上記の処理はどのIHVでもスケールして動作 最後にもう一度ソフトウェアラスタを行います。 再度描画した線分のindexを線形バッファに格納します。 格納先は、FroxelのオフセットアドレスとInterlockedから確定します。 このような無駄が多い動作ですが、この動作はどのIHVでもスケールするため対応を行いました。 17 *Froxel = Frustum + Voxel の造語 ©CAPCOM 17
ソフトウェア半透明描画 単一のCompute Shaderでラスタライズを実行 • 16x16ピクセルを1タイルとしタイル単位でGroup Threadが動作 • 1スレッドが毛の1線分を担当 • 線分の各頂点の幅を利用し、投影面積から半透明率を算出 • 線分描画時は各頂点の深度から深度テストも実行 • 照明結果は線形補間した頂点カラー • 線分の描画はアンチエイリアシングラインを使用し2D的に処理 • きれいなアンチエイリアシングを実現 • Froxelを手前から順にラスタライズ • タイルが十分不透明になったら処理を打ち切れるため ソフトウェア半透明描画は、単一のCompute Shaderでラスタライズを実行します。 16x16ピクセルを一つのタイルとし、タイル単位でグループスレッドが動作します。 18 1スレッドは、一つの線分を担当します。 線分の頂点に含まれる幅を利用し、投影面積から半透明率を算出します。 線分の描画自体はAntialiasing Lineを使用して2D的に描画し、きれいなアンチエイリアシングラインを描画します。 Froxelを手前から順にラスタライズを行います。 タイルの内部のピクセルが十分な不透明になったら打ち切るために行っています。 ©CAPCOM 18
順不同半透明 半透明描画の表現をするためにMulti-Layer Alpha Blending(以下MLAB)を実装 • Order Independent Transparency(OIT)の一種 MLAB • N個の作業バッファ(レイヤ)を保持 • 投入したピクセルの深度を利用し、手前から順に色などを保持する一種のソートを実行 • ソートから飛び出したものは、最後のレイヤの部分と半透明合成 • RE4は8レイヤを利用しいるため、実質7レイヤまでは深度と色が保証 利点 • シンプルな実装 • 固定のメモリサイズ 欠点 • Atomic性の保証 元の実装はPixel Sync(RasterOrderedView)が必要 ラスタライズする点は半透明で、深度と色を持っています。 これを単純に処理の順番に描画すると見た目が破綻します。 そのため、半透明を正しく表現するために2014に発表されたMulti-Layer Alpha Blendingを実装しました。 これは順不同半透明(Order Independent transparency)の一種です。 19 Multi-Layer Alpha Blendingは、シンプルなアルゴリズムです。 N個のバッファを保持し、投入したピクセルの深度を利用し、手前から順に色などを保存します。 これは、ある種のソート処理に相当します。 ソートから飛び出したものは、最後のレイヤーの部分と半透明合成を行います。 ©CAPCOM 19
順不同半透明 半透明描画の表現をするためにMulti-Layer Alpha Blending(以下MLAB)を実装 • Order Independent Transparency(OIT)の一種 MLAB • N個の作業バッファ(レイヤ)を保持 • 投入したピクセルの深度を利用し、手前から順に色などを保持する一種のソートを実行 • ソートから飛び出したものは、最後のレイヤの部分と半透明合成 • RE4は8レイヤを利用しいるため、実質7レイヤまでは深度と色が保証 利点 • シンプルな実装 • 固定のメモリサイズ 欠点 • Atomic性の保証 元の実装はPixel Sync(RasterOrderedView)が必要 RE4では8個のレイヤを利用しているため、7個までは深度の順番が保証されます。 この方法の利点は、シンプルな実装です。 また、固定のメモリサイズで動作します。 20 代わりに操作するためにはAtomic性の保証が必要になります。 もともとはRaster Ordered Viewといった機能を利用していました。 ©CAPCOM 20
MLABの実装 GroupSharedMemory上に作業バッファを実装 • GroupSharedMemoryは超高帯域 • 深度、半透明率、色の5要素を64bitに圧縮して保持 • 16bit depth + 16bit Transmittance + 32bit Color(R11G11B10Float) Atomic性の保証 • 64bit の InterlockedMaxに置き換え • 半透明合成 • CompareAndSwapとLoopによる実装 今回は、GroupSharedMemory上に、作業バッファを実装しています。 GroupSharedMemoryはコンソールにおいては超広帯域なので、通常のメモリ書き出しより高速に動作します。 21 次に、Atomicを保証するために、 64bitに深度と半透明率と色の合計5要素を圧縮して保持しました。 これによりAtomic性の保証は、InterlockedMaxに置き換えることで実現しています。 また、半透明合成は、CompareAndSwapとLoopによる実装です。 ©CAPCOM 21
Atomic性の保証 64Bit InterlockedをサポートしているGPU InterlockedMax Or Min(Depthの方向次第)でソート完了 for (int i = 0; i < OIT_LAYER_COUNT; i++) { InterlockedMax(image[x][y].layer[i].u64, fragment.u64, old_fragment.u64); if (compare(fragment, old_fragment)) fragment = old_fragment; } Atomic性の保証のために、シンプルに64Bit Interlockedを使います。 RE ENGINEはReverse Depthを利用しています。 22 手前に高い値が入るようになるためInterlockedMaxを使って、ソートと値の交換を行います。 下記のような感じで動作しています。 ©CAPCOM 22
64bit Atomicをサポートしない場合 32Bit InterlockedのみサポートしているGPU • 完全性はないが情報を交換することで伝搬させる • 正しくないかもしれないが色情報が消失しないようにする for (int i = 0; i < OIT_LAYER_COUNT; i++) { InterlockedMax(image[x][y].layer[i].u32.y, fragment.u32.y, old_fragment.u32.y if (compare(fragment, old_fragment)) { InterlockedExchange(image[x][y].layer[i].u32.x, f.u32.x, old_fragment.u32.x fragment = old_fragment; } あるハードウェアによっては、64Bit atomicをサポートしていない可能性があります。 32Bit InterlockedのみサポートしているGPUでは、完全ではないものの正しそうな結果になるようにしています。 23 対応は、まず32bitでInterlockedMaxを行います。 結果が現在のFragmentと比較して小さければ、新しい色にInterlockedExchangeをして交換を行います。 InterlockedMaxとInterlockedExchangeの間に時間差があります。 結果が不定になる可能性がありますが、情報を伝搬させるために交換をします。 これは正しいのでしょうか? 何もしないよりはまし、ということでこの対応を入れています。 ©CAPCOM 23
半透明の合成
Loopとcompare and swapで実装
Multi-Layer Alpha Blendingの最後は単純な合成を実行
float4 c1 = getMLABFragmentColor(fragment);
uint count = 0;
do{
Fragment f0 = imageLayer[x][y].layer[OIT_LAYER_COUNT - 1];
float4 c0 = getMLABFragmentColor(f0);
float4 mergedColor;
mergedColor.rgb = c0.rgb + c1.rgb * c0.a;
mergedColor.a = c0.a * c1.a;
Fragment v = setupMLABFragment(mergedColor, getMLABFragmentDepth(f0));
Fragment ret;
InterlockedCompareExchange(
imageLayer[x][y].layer[OIT_LAYER_COUNT - 1].u64,
f0.u64,
v.u64,
ret.u64);
count += (ret.u64 != f0.u64) ? 1 : 0xfff; //交換が成立したら終了、成立しなければループ
} while ( count < 256); //適当に打ち切る
さて、最後にレイヤーの外に飛び出した部分への対応です。
Multi-Layer AlphaBlendingの最後のバッファの光合成は、CompareAndSwapを使った半透明です。
24
この最後のレイヤー以降は部分では正しい半透明のブレンド結果にならない可能性が高くなります。
ですが、結果は大体いい感じに動いてそうです。
これは髪の毛が大体同じ色で構成されているためでしょう。
©CAPCOM
24
OIT 64bit Interlocked 64BitInterlockedでつくったOITです。 いい感じに見えます 25 ©CAPCOM 25
OIT 32bit Interlocked 32BitInterlockedでつくったOITです。 大体いい感じに見えます。 26 ©CAPCOM 26
最終バッファへの合成 半透明を事前に計算しているためTAAの動作を停止 • Responsive AntialiasingとTransmittanceの閾値を利用した深度を出力 • 深度を利用して適当な関節(頭か首)からモーションベロシティを計算 作り出されたソフトウェアラスタライザの結果を合成します。 ソフトウェアラスタライザは既にアンチエイリアシングされています。 TAAによる不要なぼかしが起こらないように、TransmittanceをResponsive AAとして使います。 こうしないと過剰なゴーストが発生し見た目が不安定になります。 27 また、Transmittanceの閾値で深度の出力のON・OFFなども入っています。 被写界深度といったポストエフェクトに反映するために行っています。 出力する際の深度は、最も手前の髪の毛の深度を採用しています。 最後にMotion Blurへの対応です。 Software Rasterizerは、単純な特定関節を利用してモーションベロシティを計算しています。 髪の毛の動きは反映しませんが、首の動きを反映することでベロシティの処理時間を稼いでいます。 (というかモーションベロシティを入れる余裕がありません) ©CAPCOM 27
パフォーマンス PS5 1920p@ CBR PS5 2160p@ CBR セットアップ 0.478ms 0.526ms ソフトウェア描画 2.890ms 2.986ms ハードウェア描画 0.285ms 0.496ms 合計 3.653ms 4.008ms では、パフォーマンスです。 右のようなカットシーンでのラスタライズだけの時間をみましょう。 4kのCheckerBoardRenderingで、おおよそ4.0msの時間をかけ髪の毛を描画しています。 28 framerate優先では、おおよそ3.6ms程度の処理時間をかけています。 これにキャラクターの数や、シミュレーションとライティングの計算が必要になります。 これらはすべてGPUの処理時間でCPUは何もしていません。 髪の毛は表示面積で性能をかなり消費しますが、見た目はいい感じです。 ©CAPCOM 28
最適化 LOD 髪の毛のLODは減らす機能を実装 • 乱数で髪の毛の順番をシャッフルしLODの割合で表示量を変更 • LODの割合で毛の厚みをスケーリング • 非カットシーンのインゲーム中は髪の毛の量を削減しゲームプレイを優先 100 % 50 % 12.5 % 25 % 6.25 % 最後に最適化です。 髪の毛は、先ほどの例からもGPUの負担が高い処理です。 29 最適化のために、髪の毛の表示割合を減らすと毛を太らせる機能をいれました。 これにより、見た目の一貫性と性能の改善を行いました。 RE4のインゲーム中は、パフォーマンス優先のためコンソールでは髪の毛を常時70%オフにして表示しています。 ©CAPCOM 29
まとめと今後の課題 高品質な毛のレンダリングが可能になった • アンチエイリアシング • 髪の毛の太さにも対応 今後の課題 • 毛の分割の自動化 • パフォーマンスと見た目の改善に役立つ • ハードウェア描画時のAntialiasing改善 • ConservativeRasterizerとGBAAを 組み合わせてOITとして合成する? • ソフトウェア描画の更なる高速化 • ある程度高速だが、アルゴリズム的に最適化の 余地がある • Froxelを手前から実行しているがN個に分割して 実行し合成など • レイトレーシング • 現在は非ストランドのメッシュを表示 まとめです。 ソフトウェアラスタライザを使いアンチエイリアシングの髪の毛を表現できるようになりました。 また太い髪の毛も対応しました。さらにシェーディングも高品質なものを追加しました。 30 今後の課題は、髪の毛の分割の自動化による、性能と品質の改善です。 また、ハードウェア描画時のOITも検討中です。 それ以外には、更なる高速化もありますが、レイトレーシングへの対応もあります。 現在は、非ストランドのメッシュを表示ししているため、 LODで説明したような少ない毛でレイトレーシングに反映できないかも検討しています。 ©CAPCOM 30
参考文献 How Frostbite is Advancing the Future of Hair Rendering Technology (ea.com),2020 https://www.ea.com/frostbite/news/the-future-of-hair-rendering-technology-in-frostbite Hair Shading • Strand-based Hair Rendering in Frostbite, Sebastian Tafuri ,2019 • Physically Based Hair Shading in Unreal,Brian Karis,2016 AntialiasedLine • https://en.wikipedia.org/wiki/Xiaolin_Wu's_line_algorithm Order Independent Transparency • Multi-Layer Alpha Blending,Marco Salvi and Karthik Vaidyanathan,2014 https://software.intel.com/content/dam/develop/external/us/en/documentsf/i3d14-mlab-preprint.pdf • Practical Order Independent Transparency, Johan K¨ohler, Treyarch,2016 https://research.activision.com/content/dam/atvi/activision/atvi-touchui/research/tech-reports/docs/PracticalOIT.pdf それでは、第一部の発表を終わります。引き続き第二部のStrand Lightingの発表になります。 31 ©CAPCOM 31
Strand Lighting それでは、Strand Lightingについて発表、ご紹介させて頂きます。 よろしくお願いいたします。 32 ©CAPCOM 32
アジェンダ Strand Lighting最終結果 マルチプルスキャッタリング Dual Scattering 間接光ライティング シャドウ シェーダグラフ 処理負荷計測 今後の課題と展望 アジェンダはこちらの通りです。 33 ©CAPCOM 33
Strand Lighting最終結果 まずこれから説明させて頂きますStrand Lightingの最終結果をご覧頂きたいと思います。 こちらはRE4のゲームカットシーンの一部で、レオンとアシュリーの髪の毛に対して、Strand Lightingを導入した結果となります。 34 こちらの導入により、髪の毛のリアルな彩度や奥行き・ボリューム感を表現する事が出来、 髪の毛の表現クオリティが向上いたしました。 ©CAPCOM 34
マルチプルスキャッタリング 光が複数の髪を通過しながら無数に散乱 明るい髪色は散乱に起因 無数の異なる光路の寄与計算が必要 リアルタイムでは実現困難 Eye それでは、まず髪のシェーディングの根本的な概念として、マルチプルスキャッタリングの説明を行います。 右図は縦の茶色のストランドヘアに太陽からの光が入り、散乱していく様子を表しています。 35 右図の通り、人間の髪は光が髪の毛を通過しながら無数に散乱する複雑な物理的多重散乱現象が起こり、視点に光が入射します。 これは、リアルな奥行き・ボリューム感を得るために重要な効果になります。 特に明るい色の髪はこの散乱効果により、明るい髪に見えているため、明るい髪にとっては特に重要な構成要素となります。 ただし、実現するためにはすべての髪の毛に対し、無数の散乱シュミレーションの計算を行う必要があり、 特にゲームのようなリアルタイム性が要求されるものでは実現が困難な計算となります。 ©CAPCOM 35
マルチプルスキャッタリング マルチスキャッタリング有無比較 マルチプルスキャッタリングなし マルチプルスキャッタリングあり こちらは、RE4のレオンのマルチプルスキャッタリング有無の比較となります。 左がマルチプルスキャッタリングなしの場合で、明るい髪色の表現がおらず、 奥行、ボリューム感がなく不自然な自然な見た目になっています。 36 一方、右はマルチプルスキャッタリングありの場合で、明るい髪色のリアルな彩度の表現や、 奥行き・ボリューム感が表現できていることがわかります。 ©CAPCOM 36
Dual Scattering マルチプルスキャッタリングを近似 Global Multiple Scattering ・Global Multiple Scattering(Ψ𝐺 ) Ψ𝐺 (𝑥, ω𝑑 , ω𝑖 ) ≈ 𝑇𝑓 (𝑥, ω𝑑 ) 𝑆𝑓 (x, ω𝑑 , ω𝑖 ) 𝑆𝑓 (𝑥, ω𝑑 , ω𝑖 ) = g(θℎ , 𝜎2𝑓 (𝑥, ω𝑑 ))/(π cosθd) 𝑇𝑓 𝑥, ω𝑑 ≈ 𝑑𝑓 𝑎𝑓 (θ𝑑 )𝑛 σ2 𝑓 𝑥, ω𝑑 ≈ β2 𝑓(θ𝑑 )𝑛 × n ・ Local Multiple Scattering(ΨL) ΨL(𝑥, ωd, ωi)fs(ωi, ωo) ≈ dbfback(ωi, ωo) fback(ωi, ωo) = 2Ab(θ)g(θh − ∆b(θ), σ2b(θ)) / πcos2θd Eye 参考論文 ・Dual Scattering Approximation for Fast Multiple Scattering in Hair ・Efficient Implementation of the Dual Scattering Model in RenderMan Local Multiple Scattering それではマルチスキャッタリングの実装についてご説明いたします。 先ほどお伝えしました通り、マルチスキャッタリングは計算負荷が高く、 ゲームではそのままでは実装が困難なため、近似手法を用いる必要があります。 37 今回RE4では、DualScatteringという近似手法を用いてマルチスキャッタリングを実装いたしましたので、そのご説明をいたします。 DualScatteringは、MultipleScatteringを2つの構成要素、 GlobalMultipleScatteringとLocalMultipleScatteringに分けて計算する事で、 複雑な物理的多重散乱現象の計算を簡略化する手法となります。 GlobalMultipleScatteringは、右図の青枠の通り、光が髪に入った所からシェーディングポイントまでの散乱を近似計算します。 その中で影響寄与が大きい前方散乱のみを扱うようにしたり、通過する髪の毛の向きを同じ方向と仮定したりすることで、多くの計 算を事前計算できるようにしています。 ©CAPCOM 37
Dual Scattering マルチプルスキャッタリングを近似 Global Multiple Scattering ・Global Multiple Scattering(Ψ𝐺 ) Ψ𝐺 (𝑥, ω𝑑 , ω𝑖 ) ≈ 𝑇𝑓 (𝑥, ω𝑑 ) 𝑆𝑓 (x, ω𝑑 , ω𝑖 ) 𝑆𝑓 (𝑥, ω𝑑 , ω𝑖 ) = g(θℎ , 𝜎2𝑓 (𝑥, ω𝑑 ))/(π cosθd) 𝑇𝑓 𝑥, ω𝑑 ≈ 𝑑𝑓 𝑎𝑓 (θ𝑑 )𝑛 σ2 𝑓 𝑥, ω𝑑 ≈ β2 𝑓(θ𝑑 )𝑛 × n ・ Local Multiple Scattering(ΨL) ΨL(𝑥, ωd, ωi)fs(ωi, ωo) ≈ dbfback(ωi, ωo) fback(ωi, ωo) = 2Ab(θ)g(θh − ∆b(θ), σ2b(θ)) / πcos2θd Eye 参考論文 ・Dual Scattering Approximation for Fast Multiple Scattering in Hair ・Efficient Implementation of the Dual Scattering Model in RenderMan Local Multiple Scattering 赤字の式がその近似式で、光源からシェーディングポイントまでの髪の本数n項以外は事前計算が可能となります。 一方のLocalMultipleScatteringは、右図の緑枠の通り、シェーディングポイントの近傍での散乱を近似計算するものです。 38 特に明るい色の髪質の場合、髪の色に大きく影響するのがこの項目になります。 GlobalMultipleScatteringが前方散乱を取り扱うため、この項は後方散乱を少なくともひとつは含む必要があります。 簡略化のため、周囲にヘアが存在すると仮定し、シェーディングは同じ方向として扱う近似を行う事で、 こちらも事前計算を行う事ができます。 これらのDualScatteringに興味のある方は下記の参考文献をご覧頂ければと思います。 ©CAPCOM 38
Dual Scattering Global Multiple Scattering計算の髪の本数n項 ・ForwardScatteringMapsを活用 - ボクセルサイズは128 x 128 x 128 - シェーディングポイントから光源までVoxelをレイシューティング →近似式からボクセルにTf、σ2f項を格納する必要がなく髪の不透明度のみ描画 →光源毎にDeepOpacityMapを作成する必要がない 頭や体のスキャッタリングの遮断 ・Gbufferの深度と奥行オフセット(30cmなど)までの間を遮断 ライティング計算はストランドヘアの頂点単位 次に先ほどのGlobalMultipleScatteringの計算で出てきましたシェーディングポイントから光源までの髪の毛の本数n項につきまして は、参考論文記載のFowardScatteringMapsを活用しています。 ただし、VoxelにTf,σf2乗は保持せず、髪の毛の不透明度を描画する方法で計算しています。 39 不透明度はVoxelにおける髪の毛の太さからの割合を0~1に正規化して加算描画しています。 VoxelSizeは128x128x128を使用しています。 シェーディング時には、シェーディングポイントから光源までのVoxelをレイシューティングし、 髪の毛の不透明度の合計を取得して髪の毛の本数に変換しています。 ©CAPCOM 39
Dual Scattering Global Multiple Scattering計算の髪の本数n項 ・ForwardScatteringMapsを活用 - ボクセルサイズは128 x 128 x 128 - シェーディングポイントから光源までVoxelをレイシューティング →近似式からボクセルにTf、σ2f項を格納する必要がなく髪の不透明度のみ描画 →光源毎にDeepOpacityMapを作成する必要がない 頭や体のスキャッタリングの遮断 ・Gbufferの深度と奥行オフセット(30cmなど)までの間を遮断 ライティング計算はストランドヘアの頂点単位 なお、DeepOpacityMapではなく、こちらを選択した理由としましては、光源毎にシャドウマップ描画及びその後の複数レイヤーの DeepOpacityMapを作成する必要がなく、ジオメトリ描画コストを抑える事ができたり、また先ほどの近似式によりTf,σf2乗を保持 する必要がなく、髪の毛の不透明度のみの描画で良いため、扱いやすく、メモリ的にも有利だと考えたためとなります。 40 また、頭や体のスキャッタリングの遮断は、Gbufferの深度から奥行オフセットの間を散乱遮断エリアとして扱っています。 なお、ライティング計算はストランドヘアのセグメント頂点単位に計算しています。 ©CAPCOM 40
Dual Scattering スキャッタリングの遮断比較 遮断なし 遮断あり こちらはそのスキャッタリングの遮断について比較画像になります。 左が遮断なし、右がGbufferの深度を用いた遮断を行ったものとなります。 髪の毛のライティングが頭や体でしっかり遮断されている事がわかると思います。 ©CAPCOM 41 41
Dual Scattering Dual Scattering有無の比較 Single Scattering Dual Scatrering 疑似Scattering(ポリゴン) 次にDualScattering処理がありなしでどう変化するのかをご紹介します。 左画像はSingleScattering単一散乱のみで、真ん中がDualScatteringの画像となります。 また、右の画像はポリゴンヘアで疑似的な異方性スペキュラやリムライト処理を行ったものとなります。 42 左画像のSingleScatteringのみではMultipleScatteringの複数の散乱計算を行わないため、 散乱による減衰がなく全体的にノッペリし、多重散乱がないため髪色が表現できていません。 真ん中の画像のDualScatteringでは、他の2つと比べて、散乱による減衰があり、 リアルな彩度や奥行き・ボリューム感が表現され、なめらかで自然な見た目になっているかと思います。 ※ポリゴンヘアは擬似的な異方性スペキュラにアルベドに加算することで表現。 逆光時のリムライト表現は、それ用にベイクしたノーマルマップを用いてリム部分にトランスルーセンシなどを適用。 ©CAPCOM 42
間接光ライティング Radiance LightProbe、LocalCubemap、IBLに対応 カメラ視点を元にした2方向 ・ Normal + Binormal方向 ・ Normal - Binormal方向 Normal Tangent Radiance Binormal 間接光のライティングの対応についてご紹介いたします。 ・間接光はLightProbe、LocalCubemap、IBLを扱っています。 43 ・全周からのそれらの間接光のライティングを、マルチプルスキャッタリングでそれぞれ計算する事はリアルタイムには困難なため、 近似実装しています。 ・手法としましては、カメラ視点を元に間接光方向を決めています。 右図のようにストランドヘアの方向をTangent、カメラ方向をNormal、それらを外積したBinormalの正規直交ベクトルを作成し、 BinormalとNormal方向の加算方向とその逆方向の2方向で マルチスキャッタリングの光源計算を行う手法になります。 ©CAPCOM 43
シャドウ ストランドヘアに落ちるシャドウ ・課題 頂点単位でのライティングのため、 ピクセルディザーの半影がチラつく。 ・対応 - ExponentialShadowMaps 半影がなめらかに髪に落ちる ストランドヘアのシャドウ対応についてご紹介いたします。 まずは、ストランドヘアに落ちるシャドウの対応のご説明となります。 44 当初ストランドヘアに落ちるシャドウは、不透明物と同様にスクリーンスペースのピクセルDitherを用いた手法で シャドウイングをしていましたが、ライティングが頂点単位のため、 視点や髪が少しでも動くと半影(はんえい)箇所で激しくちらつきが起こってしまいました。 その対処としまして、ExponentialShadowMapsを行い、影のグラデーションをなめらかにすることで課題を解消いたしました。 ExponentialShadowMapsはStrandのセグメント頂点毎にシャドウマップのフェッチが1回という事もあり、負荷的にも有益でした。 ©CAPCOM 44
シャドウ ストランドヘアが落とすシャドウ ・シャドウマップにストランドヘアをライン描画 ・描画するヘアの本数を少なくする(デフォルト20%) 次にストランドヘアが落とすシャドウについてのご説明となります。 シャドウマップに対して、シェーダ内でストランドヘアのセグメント頂点をライン描画を行っています。 45 シャドウマップに描画するストランドヘアのピクセル領域は小さく、そこまで精度が必要ではないため、 描画するライン本数を減らして描画して処理負荷を抑えています。 デフォルトでは、20%のストランド本数でシャドウマップに描画しています。 ©CAPCOM 45
シェーダーグラフ ストランドヘアの頂点セグメント単位の シェーダーグラフ BaseColorで髪色を指定 BaseColorから吸収率(Absorption)へ変換 法線、Roughnessもエディット可能 参考文献 A Practical and Controllable Hair and Fur Model for Production Path Tracing ストランドヘアのシェーディングのマテリアルシェーダについてのご紹介になります。 ・他のシェーダと同様、ストランドヘアもアーティストが扱えるマテリアルシェーダの対応を行っています。 46 ・ストランドヘアの頂点セグメント単位にシェーダのエディットが可能となっています。 ・髪色の入力として、BaseColorを扱えるようにしています。 内部的に、Absorption(吸収率)に変換してマルチプルスキャッタリングの計算で用いています。 こちらの変換の詳細にご興味がある方は、下記の参考文献をご確認頂ければと思います。 ・また主な入力パラメータとして、法線と各種Roughness値も取り扱っています。 ©CAPCOM 46
処理負荷計測 スポットライト10灯 環境光はカメラ方向を元にした2方向 PS5のGPU処理負荷 ( ライティング処理のみ ) Dual Scattering ストランド本数:23,115 セグメント頂点数:266,937 シャドウマップなし 約1.32ms シャドウマップあり 約2.25ms DualScatteringの処理負荷についてご紹介いたします。 計測の条件としましては、スポットライトが10灯、 環境光は先ほどお話しましたカメラ方向を元にした2方向でライティングを行っています。 47 計測はPS5でライティングのみの計測結果となります。 ストランド本数は2万3千本、そのセグメント頂点数は約27万頂点で、シャドウマップなしの場合、GPU処理負荷は約1.32ms、 シャドウマップありの場合は約2.25msとなります。 ©CAPCOM 47
今後の課題と展望 ライティングする髪のセグメント頂点のシームレスな増減対応 レイトレーシング環境光のライティング対応 レイトレーシング用のAccelerationStructureへの対応 コンタクトシャドウへの対応 間接光ライティングのクオリティアップ及び最適化 今後の主な課題と展望をお話いたします。 ・1つ目は、ライティングする髪のセグメント頂点のシームレスな増減の対応です。 48 ライティング用のストランドヘアのセグメント頂点を別途準備し、シームレスに変更できるようにすることで、 ライティング品質と負荷のバランスが取れるようにしたいと考えています。 この手法の導入でLODにも対応出来、有用だと考えています。 ・2つ目は、レイトレーシング環境光のライティング対応です。 こちらは、レイトレーシングを作成した環境光をストランドヘアに適用したいと考えています。 ・3つ目は、レイトレーシング用のAccelerationStructureへの対応です。 こちらは、レイトレーシングで環境光を作成する際、ストランドヘアの影響も与えるようにする対応となります。 ©CAPCOM 48
今後の課題と展望 ライティングする髪のセグメント頂点のシームレスな増減対応 レイトレーシング環境光のライティング対応 レイトレーシング用のAccelerationStructureへの対応 コンタクトシャドウへの対応 間接光ライティングのクオリティアップ及び最適化 ・4つ目はコンタクトシャドウへの対応です。 こちらは現状、髪と顔の細かな影がなく、接地間が乏しいため、対応したいと考えています。 49 ・5つ目は間接光ライティングでご説明した近似手法ですが、 まだまだクオリティが乏しい所がありますのでさらなるクオリティアップ、最適化を行えればと考えています。 ©CAPCOM 49
参考文献 ・Dual Scattering Approximation for Fast Multiple Scattering in Hair, Arno Zinke, 2008 http://www.cemyuksel.com/research/dualscattering/ ・Efficient Implementation of the Dual Scattering Model in RenderMan, Iman Sadeghi, 2010 https://media.disneyanimation.com/uploads/production/publication_asset/24/asset/2_DualScatteringImplementation.pdf ・A Practical and Controllable Hair and Fur Model for Production Path Tracing, Matt Jen-Yuan Chiang, 2016 https://media.disneyanimation.com/uploads/production/publication_asset/152/asset/eurographics2016Fur_Smaller.pdf ・Physically Based Hair Shading in Unreal, Brian Karis, 2016 ・Strand-based Hair Rendering in Frostbite, Sebastian Tafuri ,2019 ご清聴ありがとうございました。 50 ©CAPCOM 50
付録:精度 OITの色をRGB111110Floatを使用 忠実度は RGB999e5のほうがよい 追加演算コストが発生し、見た目の差も軽微なので未使用 付録ですが、OITの色をRGB111110Floatを使用しています。 忠実度は 指数部を共有したRGB999e5のほうがより良いです。 仮数部が9bitあるのでより精度の高い結果が期待できます。 ただ、この計算コストが高く、見た目も比較しないと違いがわからないため現在は未使用です。 ©CAPCOM 51 51
付録:精度 RGB111110Float FHD 52 ©CAPCOM 52
付録:精度 RGB999e5 FHD 53 ©CAPCOM 53
付録:分割 線分の投影した長さから分割数を決定 Catmull-Romで分割、RE4の製品は未導入 線分の投影した長さから分割数を決定するのも試作です。 Catmull-Romで分割することで、すべての頂点を通過する曲線を作り出せます。 分割決定は、分類時に行うことでシェーディング点と細かさを分離することもできます。 この機能はRE4の製品では未導入です。 54 投影した髪の毛の2点の線分のマンハッタン距離が、8を越えたら分割するようにしています。 またCatmull-Romは導関数もあるため、hardware rasterizerも連続した滑らかな線を可視化できます。 Catmull-Rom Centripetal Catmull–Rom spline , 2023 https://en.wikipedia.org/wiki/Centripetal_Catmull%E2%80%93Rom_spline ©CAPCOM 54
付録:分割 Catmull-Romあり 55 ©CAPCOM 55
付録:分割 Catmull-Romなし 56 ©CAPCOM 56
付録:IHV特性 安全性のためにGroupSharedMemoryはInterlockedでロードを検討 GroupSharedMemoryのロード結果をキャッシュされる可能性 • GroupMemoryBarrierWithGroupSyncを使用しない実装時に発生 • 特にCompareAndSwapのloop時に注意 float4 c1 = getMLABFragmentColor(fragment); uint count = 0; do{ Fragment f0 = imageLayer[x][y].layer[OIT_LAYER_COUNT - 1]; Fragment f0; InterlockedMax(imageLayer[x][y].layer[OIT_LAYER_COUNT - 1].u64, 0,f0.u64 ); float4 c0 = getMLABFragmentColor(f0); float4 mergedColor; mergedColor.rgb = c0.rgb + c1.rgb * c0.a; mergedColor.a = c0.a * c1.a; 幅広いGPUのサポートを検討する場合は、Interlocked命令でGroupSharedMemoryをロードすることも検討しましょう。 RE4いろいろなGPUをテストするとGroupSharedMemoryのロード結果をキャッシュされる可能性があります。 57 特に色交換のCompareAndSwapのloopでの参照時に注意が必要でした。 ただ、これは特定のGPUの特性に基づいているため、可能性として知っておくべきことなのでしょう。 ©CAPCOM 57
付録:間接光ライティング 複数方向のドミナントディレクション →最大8方向。方向が増える程、高品質及び高負荷 間接光のライティングにつきまして、検討した別手法についてご紹介いたします。 ストランドヘアの中心から全周環境光を調べて、支配的な光源方法であるドミナントディレクションを2から8の複数方向選出し、 58 その方向についてマルチスキャッタリングの光源計算を行う手法となります。 ただ、この手法では状況によっては、6方法以上でないとクオリティ的に厳しい所もあり、 RE4ではこちらの手法の採用は見送っています。 ©CAPCOM 58
付録:その他最適化手法 シミュレーション用のガイドヘアを使ったライティング ガイドヘアライティング 通常ヘアライティング その他の最適化手法についてご紹介いたします。 ・ライティングは頂点単位に行っているため、頂点の数が多くなると負荷が高くなります。 その最適化として、シミュレーション用の少ないガイドヘアを使ってライティングし、 それをルック用のストランドヘアにストアする手法を提供しています。 59 少し陰影がノッペリしてしまうため、RE4では非採用となりましたが、 かなり処理負荷が下がりますので今後インゲームなどで有用だと考えています。 (処理負荷につきまして後で紹介いたします) ©CAPCOM 59
付録:その他最適化手法 ストランドヘアの描画本数増減LOD - ストランドヘアのAABBの画面表示面積の割合(ScreenRate) を計算(画面全体を1.0とする割合) - 面積割合の最大値(RateMax)と最小値(RateMin)、 LOD最小値を加味してLOD値を割合を計算 LODLevel = (ScreenRate - RateMin) / (RateMax - RateMin) + MinLOD 𝐿𝑂𝐷値:0.1 ・2つ目は、ストランドヘアのスクリーン表示面積によって、ストランドヘアの本数の自動増減を行う機能です。 まずストランドヘアのAABBの画面表示面積の割合を計算し、表示面積の最大値と最小値、 LOD最小値を加味して下記の式からLOD値を自動で計算します。 60 これにより画面に大きく描画されているものはストランドヘアが多く、 画面に小さく描画されているものはストランドヘアが少なくなり、ストランドヘア全体の処理負荷を減らす事が可能となります。 なお、先ほどご説明した髪の毛をVoxelに描画する際、LODにより髪の本数が少なくなっている場合は、 その分不透明度を高くする事でLODによる不透明率の変化がないようにしています。 こちらは、RE4では時期的に導入を見送っています。 ©CAPCOM 60
付録:処理負荷計測 スポットライト10灯 環境光はカメラ方向を元にした2方向 PS5のGPU処理負荷 ( ライティング処理のみ ) Dual Scattering Dual Scattering(ガイドヘア) ストランド本数:23,115 セグメント頂点数:266,937 ストランド本数:1,420 セグメント頂点数:17,614 シャドウマップなし 約1.32ms 0.12ms シャドウマップあり 約2.25ms 0.18ms ガイドヘアを使ったライティングと使っていないライティングの処理負荷比較についてご紹介いたしまします。 計測の条件としましては、スポットライトが10灯、環境光はカメラ方向を元にした2方向でライティングのみの処理負荷を扱います。 61 左がDualScattering、右がガイドヘアを使用したDualScatteringです。 左側のガイドヘアを使用しないライティングでは、 ストランド本数は約2.3万本でそのセグメント頂点数は約27万頂点でGPU処理負荷は約1.32ms 右側のガイドヘアを使用するライティングでは、ストランド本数は、約1.5万本でそのセグメント頂点数は、 約1.8万頂点でGPU処理負荷は約0.12msとなります。 ガイドヘアを使用したライティングだと使用しないライティングと比べて1/10程度の処理負荷になっていることがわかります。 ©CAPCOM 61