動的に生成したGameObjectをオクルージョンカリングできません。
視界外であれば機能しているのですが、遮蔽物の先のGameObjectは描画されてしまいます。
確認すべき設定や、他に処理負荷軽減する方法はありますでしょうか?
参考までに画像を添付します。
画像では天井が無いように見えますが、存在しています。
2枚目の画像がプレイヤーの視点です。
【目的】
フィールドを自動生成しているのですが、手段としては、
・大きさが1×1×1の立方体(壁)
・大きさが0.1×1×0.1の平面(床と天井)
をいくつも生成してマップを形成しています。
プレイヤーの視界に入らないGameObjectに関しては描画されないように設定して処理負荷軽減を実現したいです。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答1件
0
ベストアンサー
視界から外れたオブジェクトが消えるのは「オクルージョンカリング - Unity マニュアル」の冒頭で言及されている錐台カリングの効果でしょうね。また、「オクルージョンカリングを使用する場合」の節には...
オクルージョンカリングを使用して動的ゲームオブジェクトを遮蔽できますが、動的ゲームオブジェクトは他のゲームオブジェクトを遮蔽することはできません。ランタイムにシーンジオメトリを生成する場合、Unity のビルトイン のオクルージョンカリングはプロジェクトに適していません。
との解説がありました。ゲーム実行中にオクルージョンデータをベイクしなおす機能は用意されていないようですので、アセットストアだとかで動的ゲームオブジェクトによる遮蔽にも対応したものを探す必要があるかもしれません。Occlusion Cullingで検索で検索してみたところいくらかあるようですが、多くは有料になってしまうみたいですね。
ですが、オクルージョンカリングより先にレンダリング負荷の軽減方法について検討した方がいいかもしれません。
たとえばメインカメラの「Clipping Planes」の「Far」はいくつになっているでしょうか?デフォルトだと1000mとかなり長く設定されているかと思うのですが、広い屋外ならともかく今回のような室内を探検するケースでは、遠くまで視線が通ることはほとんどないんじゃないかと想像されます。思い切って20とか10まで下げてしまえば錐台カリングが強力に効いてきて、大部分のオブジェクトを除外できるんじゃないでしょうか。
ファープレーンで描画が途切れて背景が見えてしまうようでしたら...
背景を真っ黒にして、さらに黒いフォグをかけてごまかせばさほど不自然にはならないかと思います。
他にも、もし壁を壊せるだとかのギミックを設けるのでしたら難しいかもしれませんが、そうではなくて常に壁は下図のようにキューブ4個積みなのでしたら...
代わりに下図のように1個の直方体にしてしまえないでしょうか。キューブ1個当たりのポリゴンは12枚ですので、ポリゴン総数を48→12に節約できるでしょう。
さらに、大部分の壁は1面だけしか見えないだろうと思います。次いで2面や3面となり、4面全部が見える壁は稀なんじゃないでしょうか。ですので壁を下図のような一枚板とし、もう一手間かけてプレイヤーから見える壁だけ生成するようにすればもっと節約できそうです。
また、床や天井にPlaneメッシュを使うのはもったいないように思います。Planeは見た目はただの一枚板ですが、実際は下図のように200個のポリゴンで構成されていますので...
これをQuadに置き換えれば、ポリゴン数を1/100に節約できるでしょう。
もしゲームデザイン上問題なければ、いっそのこと天井や床はマップ全体を覆う巨大なQuad一枚でまかなってしまってもいいかもしれません。
他には、今回のような同じメッシュを同じマテリアルで大量に描画するシチュエーションではGPUインスタンシングが有効に作用するだろうと思います。マテリアルがインスタンシングに対応しているようなら、「Enable GPU Instancing」をオンにすれば効率化できるでしょう。
おそらくこれだけやればだいぶレンダリング負荷を軽減できるんじゃないかと思いますが、まだ足りなければオクルージョンカリングを検討することになるでしょうね。今回のような室内の迷路を探索するシチュエーションなら本格的なオクルージョンカリングでなくても、マップを真上から見たときの二次元で遮蔽判定するだけでわりといけるんじゃないか...と思いまして、試しに下図のようなシェーダーで二次元的な遮蔽判定を行ってレンダラーのオン・オフを切り替えたところ(他に実際にシェーダーを動かすためのスクリプトもあるのですが、本筋でない部分のコードがかさんで長くなってしまったため省略しています)...
ShaderLab
1Shader "Hidden/RenderVisibility" 2{ 3 Properties 4 { 5 _MainTex ("Texture", 2D) = "white" {} 6 _NormalizedAlwaysVisibleRangeRadius ("Always Visible Range Radius", Range(0.0, 100.0)) = 2.0 7 } 8 SubShader 9 { 10 Cull Off 11 ZWrite Off 12 ZTest Always 13 14 CGINCLUDE 15 #include "UnityCG.cginc" 16 17 struct appdata 18 { 19 float4 vertex : POSITION; 20 float2 uv : TEXCOORD0; 21 }; 22 23 struct v2f 24 { 25 float2 uv : TEXCOORD0; 26 float4 vertex : SV_POSITION; 27 }; 28 29 v2f vert(appdata v) 30 { 31 v2f o; 32 o.vertex = UnityObjectToClipPos(v.vertex); 33 o.uv = v.uv; 34 return o; 35 } 36 37 sampler2D _MainTex; 38 float4 _MainTex_TexelSize; 39 float _NormalizedAlwaysVisibleRangeRadius; 40 float4 _NormalizedCameraPositionAndDirection; 41 float _CosineOfHorizontalFov; 42 ENDCG 43 44 Pass 45 { 46 CGPROGRAM 47 #pragma vertex vert 48 #pragma fragment frag 49 50 fixed4 frag(v2f i) : SV_Target 51 { 52 float2 d = i.uv - _NormalizedCameraPositionAndDirection.xy; 53 float sqrDistance = dot(d, d); 54 float sqrRadius = _NormalizedAlwaysVisibleRangeRadius * _NormalizedAlwaysVisibleRangeRadius; 55 if (sqrDistance <= sqrRadius) 56 { 57 return 1.0; 58 } 59 d /= sqrt(sqrDistance); 60 if (dot(d, _NormalizedCameraPositionAndDirection.zw) < _CosineOfHorizontalFov) 61 { 62 return 0.0; 63 } 64 float2 step = -d * _MainTex_TexelSize.xy; 65 float2 p = i.uv + step; 66 float2 rp = p - _NormalizedCameraPositionAndDirection.xy; 67 float o = 0.0; 68 float previousSqrDistance = sqrDistance; 69 sqrDistance = dot(rp, rp); 70 [loop] 71 while ((o < 1.0) && (sqrDistance < previousSqrDistance) && (sqrDistance > sqrRadius)) 72 { 73 o += tex2Dlod(_MainTex, float4(p, 0.0, 0.0)).r; 74 p += step; 75 rp = p - _NormalizedCameraPositionAndDirection.xy; 76 previousSqrDistance = sqrDistance; 77 sqrDistance = dot(rp, rp); 78 } 79 return 1.0 - floor(o); 80 } 81 ENDCG 82 } 83 84 Pass 85 { 86 CGPROGRAM 87 #pragma vertex vert 88 #pragma fragment frag 89 90 fixed4 frag(v2f i) : SV_Target 91 { 92 float2 d = _MainTex_TexelSize.xy * 0.5; 93 float v = dot(float4( 94 tex2Dlod(_MainTex, float4(i.uv - d, 0.0, 0.0)).r, 95 tex2Dlod(_MainTex, float4(i.uv + float2(-d.x, d.y), 0.0, 0.0)).r, 96 tex2Dlod(_MainTex, float4(i.uv + float2(d.x, -d.y), 0.0, 0.0)).r, 97 tex2Dlod(_MainTex, float4(i.uv + d, 0.0, 0.0)).r), 1.0); 98 return saturate(ceil(v)); 99 } 100 ENDCG 101 } 102 } 103}
下図のようになりました。ゲームビューの左上にあるのはデバッグ用に表示した遮蔽判定状況図で、赤が壁、緑が可視と判定された部分です。一応は見える部分だけレンダリングされるようになったみたいですが、個々のタイルのレンダリングコストがある程度高いシチュエーションじゃないと大して効果は感じられないかもしれません(むしろカリング処理によるオーバーヘッドが上回ってしまいそうです)。
オクルージョンカリングとして使うよりも、「最初は全体マップが不明だけれど、迷路の探索を進めていくとマップ上に視界に入った領域がどんどん描き込まれていく」みたいな仕掛けのために利用できるかもしれませんね。
投稿2022/07/01 21:54
総合スコア10807
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2022/07/01 23:45