circle-loader
by
349/ 0

エンジンでの草のレンダリングを書き直す理由

1. 効果図

2. エンジンの草のレンダリングを書き直すことにした理由

プロジェクトの確立はずいぶん前のことだし、当時使ったエンジンバージョンはUnity 5.6でした。ここでは、Terrai付きの草の問題と、人ずつの問題に対した解決策について説明します。以下の問題のすべてがレンダリングを書き直すことによって解決できるわけではありませんが、主ないつくかの要件があるから書き直すことにしました。

  • 法線問題メッシュ法線なし、サーフェス法線をレンダリング法線として使用 (MeshまたはBillboardは挿片で作るから、法線マップを渡さないと、面数の制限の下で草の葉の本当の法線の方向を正しく表現するのは不可能です。このモードは基本的に光が黒くならないようにすることができ、地面は基本的に太陽光で見つけることができる半球にあります)。
  • 動的メッシュの問題一般に [Collect Detail Patches] を選択して、CPU の実行中に一連のメッシュを動的に作成します。頂点数に上限があるだけでなく、フリーズが発生します。(エンジン ソース コードを非同期に変更したら、フリーズを減らすことはできますが、計算量を減らさないと発熱しますし、発熱すると PC が発熱します 、PC の発熱が一部のデバイスのCPUのアンダークロックを引き起こします)。
  • Hi-Z低効率の問題一連のメッシュを動的に作成します。レンダリングされていないすべての草を最大限にカリングすることは不可能であり、Hi-Zカリング機能を最大化することはできません。
  • PBRマテリアルの問題。PBR レンダリングはスムーズ マップなしでは実現できず、マテリアル効果が非常に低くなります (ここで画像だけを転送することも可能です)。
  • フェードインとフェードアウトの問題フェードインとフェードアウトがサポートされていないため、120 メートル離れた草の消失と出現は、PUBG のようなディザ半透明のトランジションを実現できません。
  • LODの問題ビルトイン草はLODに対応しておらず、エンジンの変更も簡単ではありません。Meshがマージされてレンダリングされたため、LODを1つずつ切り替えることは不可能です。
  • Shadowmaskの問題遅延レンダリング下では、草は地形のShadowmaskを取得し、それをG-bufferのRT4に計算します。最適化はここでは省略できます。
  • Shadowmapの問題草は投影することができません。
  • AOの問題SSAOは確かに少し草のAOを増やすことができますが、その精度表現の効果は明らかではなく、効果も良くありません.
  • 草の成長方向問題植生は重力に逆らって成長するはずですが, ゲーム内の草は小さな草の山を表しています。草が非常に短い場合, 草の成長方向は苔に似た表面。そうすることで、底と地形の間の直線を切断する問題を効果的に回避でき、地面とより一致して見えます。
  • Mipmapの問題草が遠くにあり、従来のMipmap計算ではカリングが多すぎて早く消えてしまいます。代わりに距離フィールド マップを使用する必要があります。
  • PreZ 効率の問題。PreZ 段階で透明度情報のみが必要であり、Albedo 精度の需要でマップを作成するのは性能にとって最も経済的ではなく、独立した Alpha マップが必要になることです。

解決方法

(1) 法線問題の解決法

下の図の上部は Unity の法線戦略であり、より平らに見えます。下部は半球のMesh法線を使っています。木や草は半球法線を使用することが多く (原理についてはくどくど説明しません)、メッシュ法線を直接使用するには、より詳細なモデリングまたは法線マップが必要であり、しかも両面レンダリングの簡単なが付いたライト論理を実現する必要があります(つまり、法線を逆にしてから明るさを計算する)。GPU Instanceの描画をサンプリングしているため、頂点には独自の法線があります。Terrain の草レンダリング方法を使用する場合は、草の頂点をBufferにしてShaderに渡し、VertexID に従って取得できます。

近くにある必要な法線はMesh法線であり、地形法線は遠くにある草の法線として使用する必要があるため、Normalにトランジションを作る必要があります。このように、草と底が遠くに統合されるようになります。

(2) MeshおよびHi-Z問題の解決法

Meshは ComputeShader+GPU Instanceで描画され、周囲の7×7 の仮想グリッドの草データがフレームごとに ComputeShader に渡され (グリッドは 40 メートルなので、120 メートルは草で覆われます)、変更されたグリッドのみが送信され、一つのフレームに一つのグリッドが渡されます。 ComputeShaderは、距離クリッピング、LOD 判定、フラスタム クリッピング、および Hi-Z クリッピングを、フレームごとに草のこれらの小さな領域に対して実行します。最大のかリングを達成します。この前具体的な方法を紹介したことがあります。

「Compute Shader Advanced Application: Hi-Zを組み合わせて大規模な草のレンダリングをかリングする」(中国語注意)

(3) PBR マテリアル問題の解決法

草のMetallic値が0(非金属)なので、Smoothマップを渡してAlbedoのaチャンネルに入れるだけで済みます。それはSmoothマップが常にAlbedoと同じPassにアクセスされるからです。また、Alpha>0 ピクセルの Smooth データのみが意味を持ち、草は通常 Alphatest を使用するため、必要な透明度は 0 ではなければ大丈夫です。したがって、同じ a チャネルに圧縮でき、後述のようにa は個別に格納されます。遅延レンダリングは統一されたポストLightingであるため、Smooth に渡しました。したがって、ここでライティングを記述する必要はありません。dot(normal, lightDir)<0 の場所で少し自己照明を追加して、バックライト サーフェスの明るさをシミュレートするだけで黒ずみを防ぎます。

 

下図の上部は Unity の統合された草のSmoothで、下部は a チャネルのSmooth マップで、効果のコントラストは特に目立ちません。これについては、冒頭のアーティストによる PBR 効果図を確認できます。

(4) フェードインおよびフェードアウト問題の解決法

他のゲーム エフェクトを詳しく観察した結果、Dithering Alphatest を使用して草の出現と消失を実装できることがわかりました。また、Unity に付属の Alphatest も距離が離れているほど、クリッピング領域の比率が増加します。エッジ比率の増加を差し引くことによって行われる消失は、ズームに似ています。自然にその効果は、Ditheringほど良くはありません。Ditheringによる変化はより均一ですが, 性能面では少し優れています。草の視距離は100 メートル以内の場合、Ditheringをお勧めします。逆に、遠すぎるとUnity付いたセットを使います。しかし、距離変化に応じてClipパラメータの関数を作ることができる効果とパフォーマンスのバランス方法があります。例えば:

clip(a -   lerp(_cutout, 1, smoothstep(hzbGrassDistance - 5, hzbGrassDistance, disCmr))); https://blog.uwa4d.com/admin/write-post.php#wmd-preview簡単clip(a-_cutout)の代わりに.

効果の対照:

これはエンジンの直接的なClip効果です

明確な接線が存在します

これは、PUBG Ryse Son of Romeなどを模倣したディザリングの半透明効果です。

停止時に網戸効果が見られる

これは距離によってClip値を動的に計算した結果です。

パフォーマンスの向上が見られたが、透明な領域が最初に消え、多くの場合、上部が最初に消えるため、効果は半透明のディザリングほど良くありません。

ディザリング半透明フェードアルゴリズム

(5) Shadowmask問題の解決法

このバージョンの GPU InstanceにはShadowmaskがなく、草もベイク処理に参加しません。これは、ライトマップ、シャドウマスク、および接続された地面が非常に一貫しており、地面に対応する 2 つの画像を直接サンプリングできるためです。ただし、この手順により、サンプリングを行わずにアルゴリズムを単純化できます。ディファード レンダリングのシャドウマスク ロジックは次のようになります。ベイクされたオブジェクトはシャドウマスク イメージをサンプリングし、スクリーン スペースのシャドウマスクを G バッファの RT4 に記録して、それがシャドウにあるかどうかを示します。動的オブジェクトがそうでない場合Lightprobe を持っている場合、影にないことを示すために白く描画されます (ベイクによって事前に計算されないため、システムは影にあるかどうかを認識しないため、影は無視されます)。 Lightprobe がある場合、近くの Probe が影にあるかどうかに応じて補間されます。 RT4 にも書き込みます。プロジェクトで Bakey ベイクを使用する場合、Probe にオクルージョン情報をベイクさせるかどうかはオプションです。チェックしないと、Mask データが失われます。

 

次に、ケースで説明します。

上記は、ライトマップ、シャドウマスク、ライトプローブを使用した単純なベイク シーンです。彼らは影の中でどのように自分自身を表現していますか?

 

  1. ベース プレート、壁、小さな立方体の 3 つの静的オブジェクトをレンダリングした後、それらは焼き付けられた Shadowmask を読み取って RT4 に書き込みます。下の図に示すように、Shadowmask が焼き付けられた 1 つのライトしかないため、赤チャンネル。ここでの表現は非常に明確で、黒は影の領域です。
  2. ボールをレンダリングし、草の山と考えて地面に置きます (下の図)。これはシアンであることがわかります (ここでは赤のチャンネルについてのみ説明します)。スクリーンショットはそれが 0 であることを示しています。このセットは、草のための Unity の Shadowmask 計算ロジックです。
  3. 問題が見つかるかもしれません: 草に対しRT4 を書くことができませんか?地表の黒い部分は黒で良いのではありませんか?どうせ平行光を使って照度を計算する前にこのピクセルのMask値を取得して Atten として使えますから。最後画面空間で照度を計算する際には、Shadowmask ベイクに参加する平行ライトが1 つある場合、シアンと黒はまったく同じ結果になります。つまり、草がどの領域を描いているかを知る必要はなく、草を描く場合は、草の背後にあるマスク (ほとんどが表面) を、自分が影にあるかどうかの判断に使用します。このように、大量の草の場合、このサンプリングが欠落していても、肉眼では目に見える改善が見られます。
  4. では、この省略に何か問題はありますか?もちろんあります。 Unity は無駄にもう一度計算することはありません。完全な正確性を考慮する必要があるため、パフォーマンスを保存することはできず、実際の状況に応じて独自のプロジェクトを選択できます。ここで少し問題があります。上り坂の草を横から見ると、草の背景は空または遠方である場合、問題が出ます。このとき、すぐ後ろで使用されているマスクはもはや表面ではなく、影の中の草が太陽のように明るく見えます。下図の赤枠の球の部分は、その後ろの白を取ると影にならないということなので、自分でサンプリングして計算してRT4に書き込めば、シアン (赤は 0) で、影にあることを意味します。しかし、シャドウマスクは少なくとも 70 ~ 120 メートル離れたリアルタイムの影の外側で発生したため、その時点で、プロジェクトの受け入れ状況によっては、側面の草の光透過がすでに非常に目立たないことがわかりました。
  5. この欠陥を受け入れて、Shadowmask のサンプリング オーバーヘッドを回避するなら、RT4 を書かないわけにはいきませんか? frag/vert モードのシェーダーはとてもシンプルで、out float4 RT4 の書き込みをコメントアウトするだけです。サーフェイスシェーダーならもっとシンプルで #pragmasurface surfaceFunction lightModel noshadowmask で十分です。
  6. Shadowmask について話しましょう。Unity の Shadowmask には非常に興味深い機能があります。Unity プログラム、メイン プログラム、さらには TA を 3 年、5 年、7 年、10 年と簡単に未完成にすることができる超オタクです。まずマスクと呼ばれるものについてですが、一般的には自分が特定の属性に属しているか、この属性のどれくらいをマスクと呼んでいるかは、レンダリングで画像を直接確認することができるため、シャShadowmaskはその属性に含まれているかどうかを表しています。Shadowmap は影にあるかどうかを記録するのではなく、カメラ空間の深度を記録します。得られた深度は影にあるかどうかを表していないため、マスクとは見なされません。

Unity 5.6 に関しては、少なくとも 4 種類のシャドウマスクを知っています。学習を逃した方は、これに基づいて具体的な内容を確認し、知識のギャップを埋めることができます。

  • 静的オブジェクトのシャドウマスク テクスチャを焼き出す
  • 動的オブジェクトのベイク処理、Lightprobe を使用したシャドウマスク補間情報の記録
  • Parallel Light の Shadowmap とは別に変換された Screenspaceshadowmask
  • G バッファでは、RT4 フルスクリーンの動的および静的シャドウマスクが計算に含まれます。

以下は、Shadowmask のサポート前とサポート後の比較です。

リアルタイム シャドウ距離外のシャドウマスク サポートのない草

リアルタイムの影の距離の外に、Shadowmask によってサポートされる草の効果を実現します。

(6) ShadowmapとContact Shadow問題の解決法

GPU インスタンスは草の描画に使用されるため、ここでは比較的単純であり、Graphics.DrawMeshInstancedIndirect を 3 回で呼び出すことができます。

  1. 30 メートル以内のインスタンスの場合、CastShadow のパラメーターを true に選択し、MeshLOD0 を送信します。
  2. LOD 距離が 30 メートル以内のインスタンスの場合、CastShadow を false パラメータとして選択し、MeshLOD0 を送信します。
  3. LOD 距離外のインスタンスの場合、CastShadow を false パラメータとして選択し、MeshLOD1 を送信します。

以下は影の有無の比較表です。一般に影の密度が遠いほど減衰されます。減衰方法は ShadowCast パスの Clip 値の変更が可能です.

Unity エンジンは影を落とす効果をサポートしていません

シャドウマップの効果を自分で実現した効果

地表面への投影を実現するためにシャドウマップを使用しますが、原理と精度の制限から、小さな葉のセルフ シャドウ効果は良くなく、ContactShadow を使用してこれを補うことができます。 ContactShadwo については、次を参照してください。

「Ubisoftクオリティを超えたコンタクトシャドウ」(中国語注意)

Shadowmap+ContactShadow 効果

(7) AO 問題の解決法

草の AO方法 についてはあまり言及されていませんが、前述のように、SSAO/HBAO+ では、良い草とその周囲のサーフェスの AO やその他の色の変化を細かく表現することはできません。草が生えている土と草が生えていない土では、毎日違う日差しが当たるため、色や微妙な植生も異なります。また、これはサーフェス テクスチャには適していません。同じサーフェスの一部には草があり、一部には草が生えていないためです。したがって、この区別と、光が干し草の山に入った後のさまざまな減衰シミュレーションをシミュレートするために、単一の太陽光による植生自体の減衰は、植生の半球法線によって単純にシミュレートできますが、この減衰は表面も暗くする必要があります。この計算はしばしば破棄されます。草の密度をオフラインでカウントし、さまざまな密度に応じてサーフェス AO マップを生成するという簡単な方法を考えました。別のシャドウマスクに似ていますが、シャドウマスクとは異なり、リアルタイム シャドウ レンジ内でも機能します。わかりやすくするために、パラメーターを誇張した後の効果は次のとおりです。

光の減衰度に変換された草の密度と高さのマップに従って、PSでガウスぼかしを追加します。

(8) 草の成長方向問題の解決法

草の成長前述のように、重力と反対方向に偏るか、地表に偏るか、植生に応じて方向を個別に設計する必要があります。一般に、細くて背の高い植物 (主に単子葉植物) は反重力成長に適しており、短くて大きな根茎 (主に双子葉植物) は地面近くでの生育に適しています。外積で計算する:

重力の重みは 1 で、植生のup方向はエンジンのデフォルトと同じです

重力の重みは 0 であり、地面の方向が植生の上方向として使用されます。

(9) ミップマップと PreZ 問題の解決法

Unity の Alphatest の Mipmap はデフォルトでノイズリダクションに問題があり、遠ければ遠いほど削除されてしまうため、SpeedTree が公式に提供しているリソースは LOD ごとに異なるマテリアル ボールを設定する必要があり、その値を設定するだけです。異なる距離でカットアウト。以前に言及したいくつかの解決策を次に示します。「距離を置いて消える AlphaTest の解決」(中国語注意)

 

しかし草の場合、このように予想外のゲインがあります。つまり、単純なバージョンのフェードインとフェードアウトがありますが、効果をより適切に変更するには、LOD を制御するか、ダイナミック クリップを計算するのが最適です。自分で価値を。フェードインとフェードアウトでは、後者の練習はここでは繰り返されません。

 

PreZ とは、Early Z の必須バージョンとして簡単に理解できます。エンジン変換プロセス中に、深度を強制的に 1 回レンダリングしてから、不透明オブジェクトを描画するときに ZTest Equal を使用して G バッファーを描画するため、クリップ プロセスは PreZ パスでのみ発生します。このパスは透明度のみを必要とし、色には注意を払いません。透明度を別の画像として保存しますが、分離するとパフォーマンスが向上するのはなぜですか?この写真はアルベドよりも小さくすることができるからです。大きく歪ませずに縮小するのはなぜですか?半透明のデータではなく、半透明の有向距離場 SDF データを使って保存するので、距離場を理解すれば、より正確な表現であることがわかります.元のデータが正確というわけではありませんが、補間により、Mipmaps サンプリングの効果が向上し、より小さなグラフが可能になります。

 

余分なヒント

ルートの透明度は、一部が壊れているかのように人工的に消去されます。この種の視覚的錯覚は、プレーヤーの脳を文脈に無頓着にさせます。

これはアートプロダクションが私にくれた草です、根は平らで醜いです

 

これは、いくつかのルートを手動で消去して作成した立体効果です

間近で見上げるときは、フローティングポジションがくずれないように少しゆっくりと下降する必要があります

 

上記は、プロジェクト全体の開発中に蓄積された草の要約であり、皆様のお役に立てば幸いです。


投稿者:偶尔不帅(偶にはカッコよくない)

https://www.zhihu.com/people/jackie-93-85-85

翻訳者:UWA Tech

 

シェアすることは大歓迎です!

無断転載・二次配布は禁止となります。

何かご意見・ごコメントがございましたら、いつでもお気軽にお問合せください。


UWA公式サイト:https://jp.uwa4d.com
UWA公式ブログ:https://blog.jp.uwa4d.com
UWA公式Q&Aコミュニティ(中国語注意):https://answer.uwa4d.com