質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Unity3D

Unity3Dは、ゲームや対話式の3Dアプリケーション、トレーニングシュミレーション、そして医学的・建築学的な技術を可視化する、商業用の開発プラットフォームです。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

Q&A

解決済

1回答

1882閲覧

動的に生成したGameObjectをオクルージョンカリングできない

coffeeman

総合スコア8

Unity3D

Unity3Dは、ゲームや対話式の3Dアプリケーション、トレーニングシュミレーション、そして医学的・建築学的な技術を可視化する、商業用の開発プラットフォームです。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

0グッド

1クリップ

投稿2022/06/25 08:43

編集2022/06/25 10:47

動的に生成したGameObjectをオクルージョンカリングできません。
視界外であれば機能しているのですが、遮蔽物の先のGameObjectは描画されてしまいます。
確認すべき設定や、他に処理負荷軽減する方法はありますでしょうか?

参考までに画像を添付します。
画像では天井が無いように見えますが、存在しています。
2枚目の画像がプレイヤーの視点です。

【目的】
フィールドを自動生成しているのですが、手段としては、
・大きさが1×1×1の立方体(壁)
・大きさが0.1×1×0.1の平面(床と天井)
をいくつも生成してマップを形成しています。
プレイヤーの視界に入らないGameObjectに関しては描画されないように設定して処理負荷軽減を実現したいです。
イメージ説明

イメージ説明

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答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

Bongo

総合スコア10807

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

coffeeman

2022/07/01 23:45

やはり、仕様として動的オブジェクトの遮蔽に対応していないのですね。。代替案の調査トライまでして頂きありがとうございます! (最後のシェーダーの件、凄すぎですね。。) 大変技術力のある内容で、後半は特に知らないことのオンパレードでしたので大変参考になります。 ・直方体化は、早速ですがマテリアルサイズを動的に拡大させる機能を実装して見栄えに影響無く対応できました。見える壁だけの生成はこれから試してみたいと思います。 ・床も大きな1つのPlaneに置き換えて処理負荷軽減されることを確認できました。この後はQuad化していこうと思います。 ・GPUインスタンシング、シェーダーのプログラミングは未経験なので調べてトライしていこうと思います。 ありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問