Terrainは名前の通り地形を作る機能です。
ちゃんと作れば、こんな感じのステージが作れます。
…地面があり、草木が茂り、地面には段差がある。
パっと見、使い方は微妙な感じですが、メイキングは参考になります。
さて、これを夢見て初期設定で色々とステージを作成すると、なんか超絶重い…なんて事はよく起こります。
例えばこのステージはドローコールくらいだと思いますか?
草木や地形も無しのシンプルなモデルです。
分かるわけがない?いいや、とりあえず考えてみましょう。思考停止は敵です。
10?
50?
100?
いいえ、832です。
他にも木々や草を何も考えずに足すと、あっという間に3000ドローコールみたいな値を計上してきます。
多少のドローコールは誤差とは言え、この量となると流石にボトルネック足りえます。個人的にはサヨナラTerrainがベストな気もしますが、色々と悪あがきをしてみます。
目次
Terrainという機能
Terrainは、基本的にハイトマップベースの地形エンジンです。地形のバッファーを元に地形の高さを表現するメッシュを生成しています。
Terrainは遠くのオブジェクトは低ポリゴン、近くのオブジェクトは高ポリゴンで描画します。
ブラシで草や木を配置します。
草や木は、一定距離が離れると消えるようになっています。
さて、さっそく見直していきます。
地形を見直す
まずは地形を見直します。
平坦を増やす
まず地形のポリゴンですが、基本的に段差のある所に作られ、特に段差が激しい所はTerrainの解像度が許す限りポリゴンが分割されていきます。
ShadedWireframeで確認するとわかりますが、左の平面のマップは殆どポリゴンが分割されず、頂点自体もそれ程多くないのに対して、右の山あり谷ありな部分では、幾つかの部分が明確に分割されており、また超点数も多く設定されています。
言ってしまえば、平面であれば1ドローコール程度で描画する事も実は可能です。ただし、山谷を沢山作るとポリゴンは積極的に分割されていき、最終的には上の832ドローコールのような凄いマップが出来てしまう訳です。
出来るだけ平坦の部分を増やす事で、分割数は大きく削られます。
Pixel Errorを増やして、表現を雑にする
一番何も変更せず出来る負荷の削減は、Pixel Errorを増やす事です。
この値を上げると、ハイトマップの精度を下げてくれて、ポリゴンやメッシュが削減されます。
なお、この値を下げることでポリゴンの切替が分かりやすく見えるようになってしまいます。
この設定の初期設定である「5」はかなり精度的には高く、よほど距離が離れないとポリゴン削減が見えず滑らかにポリゴンが削減されていましたが、100といったレベルになるとガッコンガッコン変わります。
この辺りは見栄えとのトレードオフです
Hightmap Resolutionを下げて、元々のポリゴンを削減する
ガッコンガッコン変わるのが嫌なら、最初から解像度を下げてしまえ、ホトトギス。
Terrainが表現出来る解像度を最初から下げてしまえば、良いという話です。
Terrainの初期サイズは、512㎡の地形に対して、512(+1)の解像度を持っています。
実際にそれ程の量が必要かと言われると、個人的には疑問です。256、いや129でも良いものは良いかもしれません。
特にUnityのTerrainは高さが著しく変わる地形を表現すると見た目が駄目になる残念な子なので、高さが大きく変わる部分には積極的にモデルを配置して隠す事が多く、大雑把な地形でも十分じゃないかという話もあります。
未確認ですが、たぶんTerrainColliderにも優しくなるんじゃないかな(多分
なお、解像度を変更すると今までの地形データが吹っ飛びます。
Occlusion Cullingを使用する
ポリゴンで分割されるということは、OcclusionCullingで削減されるという意味でもあります。山々があるような場合は、狙っていくとかなり描画を抑えてくれます。
特に初期設定のカメラはFarが1000、つまりOcclusion Cullingで削減しないなら、目の向いてる方向1km先のオブジェクトまで描画するという設定になっています。
Occlusion Cullingで遮蔽されている項目は描画しないようにすれば、かなり描画回数が抑えられます。
500㎡くらいのマップでは、積極的にカリングするか、カメラのFarを下げるのが良さそうです。
なお、モバイルやVRのようなCPUに余裕がない端末だとOcclusion Cullingは逆に負荷になるといったケースも聞きます。その場合はSmalestOccluderを上げる等で負荷を下げるなり、Occlusion Portalで限定するなり、HightmapResolution下げるなり。
影はMixedかBakedでベイクしておく
影の描画も馬鹿にはなりません。
Unityの初期設定では、150m先のオブジェクトまで影の為に描画…またカスケードがあるなら2~3回描画する事になります。
かといって影を描画しないと地形はかなり残念な事になります。逆に、150mでは山から落ちる影が出ないという事でもっと伸ばすかもしれません。
例えば、150m以内に33ドローコールの地形が合った場合、リアルタイムシャドウを使用していると33ドローコールが発生します。カスケードを有効にしてると、もっとです。
ということで、影を事前にベイクします。
影の負荷が最も低いのはSubtractiveです。
この設定では影をテクスチャに焼き込み、その情報を渡すだけなので描画負荷は増えません。
その代わり、影の解像度が固定です。
その為、低解像度の場合は近づかれると影が雑な事が一発でバレます。逆に高解像度にすると、ベイク時間が超伸びたり、ロード時に凄い待ち時間が発生したりするかもしれません。
Shadow Mask & Distance Shadowは、遠距離は事前に計算した影を使用、近距離はリアルタイムな影を使用します。
こちらもShadow Maskで描画している分には、影の為のDrawCallは無いです。リアルタイムな影のレンジに入ったものだけが描画されます。
Shadow Mask & Distance Shadowは近づいた際にリアルタイムな影を使用するので、影は低解像度でOKです。
上から見られるとバレますが、横から見る場合には問題無いです。
リアルタイムな影とShadow Maskの切替タイミングは初期設定では150mとかなり遠いので、60m程度にしておくと、自分の感触では違和感がないです。
なお、Terrainで配置した木は影を焼けません。
スピードレベルデザインで、カメラ近くの木は配置されるのが多いのは、この辺りが影響しているのかな~と予想します。
名無しさんありがとうございます。
- リアルタイムシャドウ:
オブジェクトを描画するために範囲内のオブジェクトでDrawCall追加発行
初期設定は150mまで描画。距離を伸ばすと負荷が上がり精度が下がる
150mより先は影無しゾーンになる
事前準備無し - Subtractive:
追加のDrawCall無しに影を描画
影の範囲はいくらでも伸ばせるが、綺麗にすればするほどロードとベイク時間が伸びる
汚い状態にすると近づくと汚い - Shadow Mask & Distance Shadow :
範囲外の影は追加のDrawCall無しに影を描画、範囲内はリアルタイムで描画
影の精度を荒くしてもある程度は良い
草を見直す
次に草を見直します。
この項目も膨大なDrawCallを要求する要因の一つです。また特に地形にミッシリ書く関係上、割り込みによるSetPass増加も引き起こします。
草はDetail Resolutionの数だけ位置を定義できます。
初期設定では、512㎡の地形に対してDetail Resolutionが1024です。なので、1㎡に4個の密度が設定できる感じです。
Detail Resolution per patchを上げる
手っ取り早いのが、草を一回で描画する範囲を広げる事です。
初期設定では8つの草群を1回のDrawCallに集約します。これを32といった値にすると、草に対する描画命令は大きく削減されます。
例えばDetail Resolutionが32のマップで、Detail Resolution per patch 16を設定すると、草は4DrawCallで描画されます。
Detail Resolutionが32のマップで、Detail Resolution Per Patch 32を設定すると、草は1DrawCallで描画されます。
なお纏めすぎると、ソレはソレで問題になるので、そこんとこ注意
あと、今まで作ったデータはたぶん飛びます。
Detail Densityを下げる
草の密度を下げます。
Detail Resolution per patchの負荷が下がるんじゃないかなと予想してますが、未検証です。
草をビルボードにする
草の表現をメッシュではなくビルボードにします。
少ない量で密度感が出ます。
ただ、カメラが上から覗き見れる場所は、ビルボードを外すが吉です。
木を見直す
最後に木を見直します。
ただ、木は多くのSpeed Level Designではそのままシーンに配置している事が多いです。ライトマップを焼けますし、色々と制御も簡単です。
Instancingを有効にする
木関連で一番効果的だったのは、Instancingを有効にする事です。
木に登録したオブジェクトの元に行き、MaterialのInstancingを有効にします。
これで木を一本一本描画していたのが、一気に複数本の木を描画出来ます。
Before
After
ただしUnity 2017以下の場合、Lightprobeの影響下にあるオブジェクトはInstancing出来ません。なので、Lightprobe For Treeを外します。
ビルボードをもっと積極的に使う
ビルボードをもっと積極的に使います。
ビルボードは大抵の場合、普通にモデルを描画するよりも安いです。
ビルボードへの切替距離は、SpeedTreeのPrefabから行います。TerrainのSettingにも似たような項目がありますが、SpeedTreeではコチラは機能しないと警告が出ます。
Before
After
ただビルボードは動く所が見られると一発でバレます。
その辺りとパフォーマンスのトレードオフになります。
関連
描画の範囲を限定するOcclusion Cullingについてです
Shadow Maskは個人的にかなり好きです。
Progressive Lightmapper(2018で正規の機能に、大規模マップにも対応)と組み合わせられるようになってから、影のベイク関係が超楽になりました。
Unity 2018でInstancingが色々方面に対応したお陰で、色々な所に使えるようになりました。
【Unity】パーティクルをGPU Instancingで描画してみる & 対応シェーダーを自作してみる - テラシュールブログ
Terrainを使って色々するのは、ここのムービーが個人的に参考になります。