- 投稿日:2021-01-22T22:13:38+09:00
【Unity】uGUIの謎を1つ1つ解明する
- 投稿日:2021-01-22T19:04:50+09:00
Unityでの開発において、WebGL用のマウス入力イベントの扱い方
前回は、Unityで回転可能なオブジェクトを作成する方法を紹介しました。オブジェクトは任意の角度からマウスでクリックしてドラッグすることで回転されます。前回の記事のリンクは下記になります。
https://qiita.com/NestVisual/items/9a97903eb9ea8918bcbe今回は、WebGL用のマウス入力イベントの扱い方について説明させて頂きます。
まずは問題となる部分から触れていきます。今回作成した機能はエディタでも、PCビルドでも問題なく動作していましたが、プロジェクトのターゲットプラットフォームをWebGL用のアプリケーションでビルドした際に問題が発生しました。ウェブブラウザには、マウスカーソルでできることに制限があり、システムターゲットのアプリケーションにはない制限があります。その一つが、コードによるカーソルの移動や位置合わせができないことです。このアプリケーションでは、カーソルは非表示になっており、2つの機能があります。デフォルトでは、マウスはファーストパーソン・シューティングゲームのようにカメラを回転させます。 しかし、カーソルがオブジェクトをクリックしてドラッグすると、カメラは回転せず、代わりにオブジェクトが回転します。 エディタでは、マウスの位置は常にアプリケーションウィンドウの中央にある十字の位置になるので、これは問題なく動作します。これは以下のFirstPersonControllerにあるコードのおかげです。
Cursor.lockState = CursorLockMode.Locked;
また、'Esc'キーを押してアプリケーションのフォーカスを解除した後、再フォーカスすると、下記のようなことが起こります。WebGL ビルドを実行すると、カーソルは少なくとも所定の位置にロックされますが、アプリケーション中央に再配置されることはありません。カーソル自体が見えないので、これは残念なことです。十字線と同じ位置には対応していない可能性が高いです。
回避方法としまして、カーソルの位置に関係なく、アプリケーション中央からレイキャストができる事が分かりました。但しこれは簡単そうに聞こえますが、この動作を直接変更はできません。独自のカーソルコントローラクラスを設定する必要があります。そこで、このクラスをUnityのMonoBehaviourマウスイベントコールバックメソッドの代わりに使います。まず、必要なマウスイベントのためのメソッドを定義したシンプルなインターフェースを作成します。
IMouseInteractive.csinterface IMouseInteractive { void OnCursorDown(); void OnCursorUp(); void OnCursorDrag(); }次に、MonoBehaviour に加えて、RotatableObject.cs にこのインターフェイスを実装させ、それを継承したメソッドを実装する必要があります。
RotatableObject.cspublic class RotatableObject : MonoBehaviour, IMouseInteractiveIMouseInteractiveメソッドの実装は、MonoBehaviourのマウスコールバックを使う場合と同じなので、メソッド名の「Mouse」の部分を「Cursor」に変更するだけです。
RotatableObject.cspublic void OnCursorDown() { if (characterController == null) return; characterController.IsLookingWithMouse = false; } public void OnCursorUp() { if (characterController == null) return; characterController.IsLookingWithMouse = true; } public void OnCursorDrag() { if (characterController == null || Camera.main == null) return; float rotX = Input.GetAxis("Mouse X") * userRotationSpeed * Mathf.Deg2Rad; float rotY = Input.GetAxis("Mouse Y") * userRotationSpeed * Mathf.Deg2Rad; transform.Rotate(Camera.main.transform.up, -rotX, Space.World); transform.Rotate(Camera.main.transform.right, rotY, Space.World); }次のステップでは、カーソルイベントを適切に発生させるCursorControllerクラスを作成します。入力を扱っているので、Updateメソッドで以下のメソッドを呼び出します。最初に CalculateCursorData()が呼ばれます。現在のカーソルの状態を記述するのに必要な値を計算します。そして、これらの値を使用して、マウスの機能が実行されているかどうかを判断します。各マウス機能は別のメソッドに構造化されています。
CursorController.csusing UnityEngine; public class CursorController : MonoBehaviour { private IMouseInteractive hoveredObject; private IMouseInteractive selectedObject; private bool isHovering; private void Update() { if (Camera.main == null) return; CalculateCursorData(); CheckMouseDown(); CheckLeftMouseUp(); CheckMouseDrag(); } private void CalculateCursorData() { RaycastHit hitInfo = new RaycastHit(); Ray ray = Camera.main.ViewportPointToRay(new Vector3(0.5f, 0.5f, 0f)); bool rayHitSomething = Physics.Raycast(ray, out hitInfo); bool hasRigidBodyAndIsInteractive = (hitInfo.rigidbody != null) && (hitInfo.rigidbody.GetComponent<IMouseInteractive>() != null); bool hasColliderAndIsInteractive = (hitInfo.collider != null) && (hitInfo.collider.GetComponent<IMouseInteractive>() != null); isHovering = (rayHitSomething) && (hasRigidBodyAndIsInteractive || hasColliderAndIsInteractive); if (isHovering) { if (hitInfo.rigidbody != null) hoveredObject = hitInfo.collider.attachedRigidbody.GetComponent<IMouseInteractive>(); else if (hitInfo.collider != null) hoveredObject = hitInfo.collider.GetComponent<IMouseInteractive>(); else hoveredObject = null; } else { hoveredObject = null; } } private void CheckMouseDown() { if (!isHovering || !Input.GetMouseButtonDown(0)) return; selectedObject = hoveredObject; hoveredObject.OnCursorDown(); } private void CheckLeftMouseUp() { if (selectedObject == null || !Input.GetMouseButtonUp(0)) return; selectedObject.OnCursorUp(); selectedObject = null; } private void CheckMouseDrag() { if (selectedObject != null && Input.GetMouseButton(0)) selectedObject.OnCursorDrag(); } }以上になります!これで今までと同じような機能を持ちつつ、WebGLアプリケーションにも対応する事が出来ました。カーソルコントローラを拡張して「CheckMouseEnter」や「CheckMouseExit」などの機能を追加することもできますが、今回は最低限の部分を実装しました。
- 投稿日:2021-01-22T17:08:01+09:00
リソースファイルが多すぎでインストールが遅くなさせる状況を最適化するにどうすれば良いですか?
今回の主な話題:Androidプラットフォームでのリソースファイルが多すぎでインストールが遅くなさせる状況の最適化方法、ShaderVariantsをパッケージするとAssetBundleが失う、Graphcis APIがAutoに切り替えると特殊効果が異常になる。
アセット管理
Q1: 私たちのプロジェクトには16,000を超えるリソースファイルがあり、Android設備でのインストールが遅くなります。そのために、他のゲームを分析したところ、すべてのファイルが1つの大きなファイルに統合され、それが読み取られることがわかりました。
マネして簡単なファイル統合を行って、すべてのファイルを一つの大きいファイルに書き込んでから、記録させたPosとLengthを介して、Seekがデータを読み取り、結構重いと感じました。このような多すぎるファイルに対して、何か良い解決策はありませんか?
①
大部分のリソースはBundleに対応しています、大きなファイルにパッケージし、ヘッダーと長さを記録するのは大丈夫ですけど、いくつ注意すべき点があります。
1、重複コストを回避するために、ハンドルを離さないでください。
2、Unityは、AssetBundleを読み取るときにoffsetのインターフェイスを提供し、使用できます。public static AssetBundle LoadFromFile(string path, uint crc, ulong offset)。
3、自己管理型ヘッダーファイルを事前に読み取り、マッピング関係を確立します。
4、マルチファイルヘッダーマッピングをサポートします。これにより、パッケージを分けてiOの速度を上げることができます。②
問題主が言ったインストールプロセスが非常に遅いことは、apkのインストールプロセスでありますか?それとも、インストール後にゲームを初めて起動する時のリソースのコピープロセスでありますか?
私たちのゲームプロジェクトのAssetBundleファイルの数は1万を越えまして、大体同じ数級です。一部のAndroidデバイスでのapkのインストール時間は確かに速くありませんが、テスト時に時間に影響を与えるだけなので、プレーヤーエクスペリエンスの統計には含まれていません。そのため、どのくらいの時間がかかるかわかりません。apkをインストールした後、解凍することを選択しなかったため、この方面で時間のコストはありません。
一つの大きなファイルへの統合はよく見える方法ですが、個人的な感覚には、C++最下層でこの仕様を直接するのは一番いいです。1セットの仮想ファイルシステムを作るみたいです。C#層でこの仕様をするのは少し面倒くさいで、意味のないメモリ占用とCPUコストをたくさん引き起こすかもしれません。一方で、問題主はProfilerでこの部分の具体的なコストはどこにありますかを確認できし、改善の余地があるかもしれません。
大きなファイルを使用したいが、offsetに基づいての読み取りは最適化スペースを見つかれない場合は、それを1つまたは複数の大きなファイルに圧縮してから、初めて実行するときに解凍することもできますが、少し長いインストール時間の方がより良いと感じます。さらに、問題主は現在のパッケージのサイズを考慮し、できる限りにいくつ合理的な合併をし、Patchのサイズをコントロールながら、AssetBundleの数を減少できます。パッケージサイズが1GBの場合、ファイルが2万あると、平均値は50KBであり、非常に小さいです。AssetBundleファイル数を1万に合併しても、平均値は100KBで、納得できます。
もちろん、そのような質問を提起した問題主のおかげで、apkのインストール時間とファイル数の間に関係があるかもしれないことに気づきました。ありがとうございます。
Shader
Q2: 私たちのプロジェクトのShaderにShader_featureを使用して、複数の変体を生成します。 エディターモードではすべて正常に動作しますが、Androidにパッケージ化すると、Shaderが失う状況は出現します。その原因は、AssetBundleがパッケージ化されたときに、Shaderの名前を独立に設定したためと推測します。モデルが収集したとき、Materialにマクロ設定があるかどうかを考慮しなかったで、Shader自体は引用されましたけど、モデルが使ったのは変体ですから失いを引き起こします。
他の方法も試しました、例えば、ShaderをAlways Included Shadersに入れること、そうすると、コンパイルが長くなり、パッケージサイズも大きくなります。Shaderのすべての変体が一度独立してコンパイルされ、apkパッケージに入れられるため、確かに問題を解決できます。次に、ShaderVariantCollectionも使用してみました、バージョンはUnity 5.6.4p4です。効果はないと感じました。Collectionに必要なパラメータを設定し、Shaderと一緒にパッケージしましたが、実機でテストするとShaderは依然として失われます。全く効果なかったと感じました。
最後に一つ方法を試しました。自分がこの変体ShaderのMaterialを全部収集し、すべてのMaterialとShaderを一つのAssetBundleにパッケージします。これで失われなくなりますが、別の問題が発生します。一つのMaterialはKeywordを設定する必要がある場合は、新しい変体Shaderが必要です。現在のパッケージがこのShaderが生成されていない場合、同じように失われます。
何か良い解決策ありませんか?
①
ShaderVariantCollectionに関して、簡単に言えば、上位バージョン(Unity 2018)では正常に使用できます。他の場合にはDummy Materialを使用することの方がより安定です。
「一つのMaterialはKeywordを設定する必要がある場合は、新しい変体Shaderが必要です。現在のパッケージがこのShaderが生成されていない場合、同じように失われます。」という問題については、良い方法がないようです。 この方法の欠点は、維持が面倒なことです。
②
ShaderVariantCollectionは自動的に収集するだけでなく、Keywordを手動で設定することもできるため、最初にすべての変体とアーティストが使用するすべての変体を確認する必要があります。後で問題を回避するために、アーティストが使用する前に制限することをお勧めします。更に、Shaderコードの分離、Lightmap関連Shaderとキャラクターの分離、LightなしとLightあるの分離、一つLightと二つLightの分離を行って、できる限りにコードの種類を減少させ、アーティストが使用できるShaderの範囲を制限します。
OpenGL
Q3: 以前のプロジェクトでは、Android Graphics APIは常に強制OpenGL ES 2.0を選択しました。最近、最適化したいの上、「Auto」を選択しました。切り替えると、ほとんどのスマホで正常に動作できし、テスト後のパフォーマンスも若干向上しましたが、現在、妙な現象があります。
1)OPPO R9、MEIZU m2e 2台のスマホでは、特殊効果には粒子のフラッシュ、拡大、消失などのさまざまな妙な現象があり、特殊効果のみに問題があり、他の画面は正常です。
2)同じリソースの場合、以前の強制OpenGL ES 2.0に戻したら正常になります。
3)うまくいかない特殊効果は、すべてUIに配置された特殊効果です。例えば、UIパネルの特殊効果や、RenderTextureによって実現されたUIモデルに関連付けられた特殊効果など、同じモデルの特殊効果は、シーンで正常に再生できます。Unity 5.3.6バージョンを使用しています。誰かが同じような問題に遭う経験ありませんか?
①
https://en.wikipedia.org/wiki/OpenGL_ES
OES_vertex_half_floatは、OpenGLES2.0の一つの拡張機能です。②
現在、ハイエンドマシンを含む多くのスマホで精度の問題が発生しています。floatを使用できる状況で、floatを使用して徐々に最適化することをお勧めします。
③
慎重に検討し、繰り返し観察した結果、問題のある特殊効果の共通点を見つけました。それは「位置」であります。特殊効果Shaderの頂点計算部分を調べてみると、大部分の特殊効果の頂点属性はhalfであります。シーン特殊効果とUI特殊効果の違いを比較したところ、UI特殊効果のワールド座標がシーン特殊効果よりもはるかに大きいことがわかりました。ですから、half精度に問題があるといつも感じていました。halfをfloatに変更し、再パッケージして検証すると、問題は解決されました。
Shader
Q4: Shaderで、実際の計算に使用していないパラメーターはGPUに入りますか?下記のようなShaderを仮定します。Property内に一つのテクスチャ_MainTextureが定義され、割り当てられています。そしてSubShaderにもテクスチャ_MainTextureを定義しましたが、実際の計算プロセスにはこのテクスチャは使用されません。
この場合、CPUはまだテクスチャをビデオメモリに入力し、CPUとビデオメモリのコストを引き起こしますか?Propertyの存在を保持しますが、SubShaderにSampler2Dを定義しない場合、このコストを回避できますか?Shader "Test/DummyTex" { Properties { _Color ("Main Color", Color) = (1,1,1,1) _MainTex ("Base (RGB)", 2D) = "white" {} } SubShader { Tags { "RenderType"="Opaque" } LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata_t { float4 vertex : POSITION; }; struct v2f { float4 vertex : SV_POSITION; }; fixed4 _Color; sampler2D _MainTex; v2f vert (appdata_t v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); return o; } fixed4 frag (v2f i) : COLOR { fixed4 col = _Color; return col; } ENDCG } } }問題主はUnityのShaderをOpenGLまたはDXのShaderにコンパイルすることを試みることができます。OpenGL ES 2.0を例にします。ShaderのInspectorパネルにあるCompileand Show Codeボタンをクリックして、表示できます。問題主が与えたShaderをGLES 2.0バージョンにコンパイルしました。
////////////////////////////////// // // // Compiled programs // // // ////////////////////////////////// ////////////////////////////////////////////////////// No keywords set in this variant. -- Vertex shader for "gles": Shader Disassembly: #version 100 #ifdef VERTEX attribute vec4 _glesVertex; uniform highp mat4 unity_ObjectToWorld; uniform highp mat4 unity_MatrixVP; void main () { highp vec4 tmpvar_1; tmpvar_1.w = 1.0; tmpvar_1.xyz = _glesVertex.xyz; gl_Position = (unity_MatrixVP * (unity_ObjectToWorld * tmpvar_1)); } #endif #ifdef FRAGMENT uniform lowp vec4 _Color; void main () { gl_FragData[0] = _Color; } #endif見られますのは、UnityのShaderコードではテクスチャサンプリングが使用されていないため、最終的に生成されたGLSLにはテクスチャサンプリング変数がありません(最適化されています)。したがって、このGLSLコードをコンパイルまたは使用する場合、テクスチャユニット変数を割り当てる必要はありません。 テクスチャメモリのコストはありません。
さらに、テクスチャがGPUに占めるビデオメモリのコストは、Shaderに使用されているかどうかに決めます。すべてのShaderがテクスチャを使用するとコストがあるではありません。例えば、Shader AがテクスチャAを使用した後に、Shader BもテクスチャAを使用しました。これで、テクスチャAはGPUに2回アップロードする必要はありません、1回だけで結構です。
アセット管理
Q5: Unity3Dエンジンの実行中に、ある圧縮形式のテクスチャを使用する場合、ロードする時に解凍してビットマップのRGBA32ビット形式に従ってメモリに保存されますか、それともメモリ内も圧縮形式のデータで、レンダリングを提出する時にGPUが自分で解凍してサンプリングしますか?GPUハードウェアが画像または動画の圧縮形式をサポートしているという記事をよく目にするのはなぜですか? この処理過程は大体どのようですか?
実際、これらは画像圧縮とテクスチャ圧縮の2つの違う概念です(用語が不正確な場合があります。訂正してください)。JPGとPNGなどは画像圧縮であり、ETC、PVRTC、ASTCなどはテクスチャ圧縮です。画像ファイルはハードディスクからメモリに読み込まれ、解凍されてテクスチャになり、GPUに転送され、GPUはそれを特定のRGBカラー値に解凍します。Read&Writeがオンになっていない場合、テクスチャはメモリからアンロードされます。
UWA Technologyは、モバイル/VRなど様々なゲーム開発者向け、パフォーマンス分析と最適化ソリューション及びコンサルティングサービスを提供している会社でございます。
UWA公式サイト:https://jp.uwa4d.com
UWA公式ブログ:https://blog.jp.uwa4d.com
- 投稿日:2021-01-22T16:59:12+09:00
CSV配置ファイルの最適化策
今回の主な話題:CSV配置ファイルの最適化策、単数回setactiveがコントローラーの初期化を導く、WWWでアセットをロードするとクラッシュする、Unityバージョンのアップグレード後にテクスチャ形式が変更され、すべての.metaが自動的に変更されます。
Monoメモリ
Q1: UWAのレポートによると、Mono GCの割り当ては30MBに達し、存続メモリは約20MBです。直接Splitすることもindexof+substringをすることも試しましたが、あまり改善されませんでした。CSV配置ファイルの最適化策ありませんか?
C#文字列は変更できないため、文字列操作によって多数の一時変数が生成することが多く、一般的な操作で回避しにくいです。問題主は、以下の二つの方法を考慮できます。
1)CSVを他のストレージ構造に変換します。例えば、protobufまたはMessagePackなど。
2)unsafeポインタを使用して文字列を操作します(少し面倒かもしれません)。基本的な考え方は、文字列が[idx0,idx1]間のcharを手動的にint、float、boolまたはstringに変更します。Stringとboolは比較的簡単で、次にint、floatにはさらに考慮事項があります。unsafeポインタを使用して文字列を操作します(少し面倒かもしれません)。基本的な考え方は、文字列が[idx0,idx1]間のcharを手動的にint、float、boolまたはstringに変更します。Stringとboolは比較的簡単で、次にint、floatにはさらに考慮事項があります。C++の経験があれば、このようなコードを書くにはより簡単はずです。次のステップは配列です。最初に配列の長さを確定して、後で配列を構造することは一部のGCを減らすことをできます。最終的な問題はDictionaryです。
アニメーション
Q2: 動作コントローラーで特殊効果を表現する時に、表示中にコントローラーの初期化が発生するため、そのフレームの時間コストが大幅に大きくなります。さらに、毎回SetActiveすると、コントローラーを初期化し、毎回も初期化の時間コストがあります。何か良い解決策ありませんか?
①
GameObjectを非表示にするために、必ずしもSetActiveが必要なわけではありません。すべてのRendererコンポーネントのEnableをFalseに設定できます。
②
Animatorコンポーネントを含むGameObjectsのActiveな操作は、間違いなくAnimator.InitializeのCPUコストを引き起こします。
クラッシュ
Q3: Unity 4.xでWWWを使用してアセットをロードしますと、クラッシュします。表示された内容は以下になります。
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR)。最後に確認した、コールされたインターフェイスは、
MemoryProfiler::RegisterRootAllocation(void*, BaseAllocator*, char const*, char const*)+124どのように処理すれば良いですか?
Development BuildバージョンでクラッシュLogが発生したら、実際には処理しなくでも大丈夫です。なぜなら、このCrashはUnity 4.x以上のDevバージョンのみで出現し、Releaseバージョンではありません。問題主がReleaseバージョンでも同じ位置でクラッシュが発生したら、原因は他の問題はずで、このCrash Logと関係ありません。このBugはUnity 5.0以降修正されましたが、Unity 4.xでまた存在しています。
アセット管理
Q4: 私たちのプロジェクトがUnity5.6から2017.4.3にアップグレードされた後、アーティストのテクスチャファイルが全部変更されました。主にMetaは自動的に変更されました。変更は次のとおりです。これは、Unityバージョン自身の問題で、テクスチャの一部の情報が変更されましたか?
問題を解決しました。この原因は、私たちのプロジェクトに後処理スクリプトがあり、OnPostprocessTextureでアーティストのテクスチャのサイズを制限し、2048を超えるテクスチャを提出することを防ぎます。インポートされたテクスチャにインポート修正をしなければ大丈夫です。
Monoメモリ
Q5: ManagedHeap.ReservedUnUsedSizeのメモリ占用は2GBで、これはなぜですか?このパラメータは一般的にどの範囲に制御しますか?
これは、ゲームが特定の段階まで実行しましたから、大きなMonoメモリの割り当てが発生し、全体的なMonoメモリを高めました。後でここのMonoメモリがGCされましたが、全体的なMonoメモリはシステムに返りません。これが、ManagedHeap.ReservedUnusedSizeが非常に大きい理由です。これに対して、問題主にこのような状況があるかどうかを確認することをお勧めします。一般的には、ゲーム、スキルリストまたは地形ナビメッシュをロードする時に発生します。これらの方面から確認することをお勧めします。
UWA Technologyは、モバイル/VRなど様々なゲーム開発者向け、パフォーマンス分析と最適化ソリューション及びコンサルティングサービスを提供している会社でございます。
UWA公式サイト:https://jp.uwa4d.com
UWA公式ブログ:https://blog.jp.uwa4d.com
- 投稿日:2021-01-22T16:10:55+09:00
[Unity]VSCodeでC#インテリセンスが効かず、 "Cannot start OmniSharp because Mono version >=x.y.z is required."と表示された場合の対処法
初投稿です。
ちょっと変なとこで躓いてイラッとしたので憂さ晴らしに記事を書くことにしました。monoの最新バージョンをbrew install monoなりでインストールしてる前提です。
VSCode内 「Ctrl+P」
setting.jsonと入力し、ファイルを開く。setting.json内の以下パスを確認。
"omnisharp.monoPath":/usr/local/Cellar/mono/xターミナルで以下を実行。
$ls /usr/local/Cellar/mono/現在のmono最新は"6.12.0.90"なんですけど、こんな感じの文字が出力されます。
出力された文字とxと一致していなかった場合は、
setting.jsonのx部分を出力文字で置き換えてください。VSCode内 「Ctrl+Shift+P」
>omnisharp: restart omnisharpこれでインテリセンスなり参照なりできるようになりました。
- 投稿日:2021-01-22T13:10:47+09:00
Unityのビルド時に関してのエラー対処
久しぶりの投稿ですが内容はとても薄い内容です。
今回はUnityのビルド時にエラーが発生してその解決方法を保存しておこうと思い書かせていただきました。ビルドがうまくできない時の対処法
いろんな記事を読んで対処してみましたが、同じような内容であったり同じエラー文でも全く違うところが悪かったりしたのでまとめておきます。
1、Unityを管理者として実行
私はこれを試したのですが、うまくいきませんでした。
2、.slnファイルを消去する
これもうまくいまんでした。
3、ビルドするときのフォルダを別のフォルダに変える
またうまくいきませんでした。
4、コードで使えないコードがあるかチェックする
私たちの場合はこれでした。
コードの内容としてはこれでした。UnityEditor.EditorApplication.isPlaying = false;内容はUnityEditorの実行を停止する処理でした。
Unityでビルドできないコードがあることは初めて知りました!!
今後気を付けてコーディングしないとですね!!
- 投稿日:2021-01-22T00:57:46+09:00
PostProcessingStackV2のシェーダで_CameraDepthNormalsTextureから深度と法線を取り出す方法
PostProcessingStackV2のシェーダ内で、深度と法線を使いたい。
けどググっても日本語の情報があまり無かったのでメモとして記事化しておきます。事前準備
Cameraの設定変更
カメラのdepthTextureModeをDepthNormalsにしておきます
Main.cspublic class Main : MonoBehaviour { [SerializeField] private Camera _mainCamera; // Start is called before the first frame update void Start() { _mainCamera.depthTextureMode = DepthTextureMode.DepthNormals; } }もしかするとこれは不要かも?
けど作業中_CameraDepthNormalsTextureが空になることが何度かあったので念の為。PostProcess設定用のcsを準備
カスタムPostProcessの追加方法については公式マニュアルの
Writing custom effects | Post Processing | 3.0.1
を見てもらうとして、とりあえず深度と法線を表示する用として下記のスクリプトを追加DepthNormals.csusing System; using UnityEngine; using UnityEngine.Rendering.PostProcessing; namespace ScreenPocket.PostProcess { [Serializable] [PostProcess(typeof(DepthNormalsRenderer), PostProcessEvent.AfterStack, "ScreenPocket/DepthNormals")] public sealed class DepthNormals : PostProcessEffectSettings { [Range(0f, 1f), Tooltip("DepthNormal effect intensity.")] public FloatParameter blend = new FloatParameter {value = 0.5f}; [Range(0f, 1f), Tooltip("Depth or Normal. 0:Depth ~ 1:Normal")] public FloatParameter depthOrNormal = new FloatParameter {value = 0.5f}; } public sealed class DepthNormalsRenderer : PostProcessEffectRenderer<DepthNormals> { public override void Render(PostProcessRenderContext context) { var sheet = context.propertySheets.Get(Shader.Find("ScreenPocket/PostProcess/DepthNormals")); sheet.properties.SetFloat("_Blend", settings.blend); sheet.properties.SetFloat("_DepthOrNormal", settings.depthOrNormal); //ビュー空間の法線をワールド空間に変換するための行列 var viewToWorld = Camera.main.cameraToWorldMatrix; sheet.properties.SetMatrix("_ViewToWorld", viewToWorld); context.command.BlitFullscreenTriangle(context.source, context.destination, sheet, 0); } } }depthOrNormalを操作することで深度と法線が見られるようにしておきます。
また、シェーダ内でView空間の法線をWorld空間に変換したいので変換用Matrixもシェーダに渡しておきますシェーダコード
で、ここからが本番のシェーダコード。
勿体ぶってもアレなので、表示するためのコードを書いておきます。DepthNormal.shaderShader "ScreenPocket/PostProcess/DepthNormals" { HLSLINCLUDE #include "Packages/com.unity.postprocessing/PostProcessing/Shaders/StdLib.hlsl" TEXTURE2D_SAMPLER2D(_MainTex, sampler_MainTex); TEXTURE2D_SAMPLER2D(_CameraDepthNormalsTexture, sampler_CameraDepthNormalsTexture); float _Blend; float _DepthOrNormal; float4x4 _ViewToWorld; inline float DecodeFloatRG( float2 enc ) { float2 kDecodeDot = float2(1.0, 1/255.0); return dot( enc, kDecodeDot ); } float4 Frag(VaryingsDefault i) : SV_Target { float4 mainColor = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoord); float4 color = SAMPLE_TEXTURE2D(_CameraDepthNormalsTexture, sampler_CameraDepthNormalsTexture, i.texcoord); float3 normal; float depth; //DecodeDepthNormal(color, depth, normal); //↑本当ならこうしたいけど #include "UnityCG.cginc" しないといけないので↓で置き換える depth = DecodeFloatRG(color.zw); normal = DecodeViewNormalStereo(color); //View空間からWorld空間に変換 normal = mul((float3x3)_ViewToWorld, normal); color = float4(lerp(float3(depth,depth,depth), normal, _DepthOrNormal),1); return lerp(mainColor, color, _Blend); } ENDHLSL SubShader { Cull Off ZWrite Off ZTest Always Pass { HLSLPROGRAM #pragma vertex VertDefault #pragma fragment Frag ENDHLSL } } }コメントにも記載した通り、#include "UnityCG.cginc" して、DecodeDepthNormal()するべきではあるのですが、HLSLコードであることと、結局必要なのはDecodeFloatRG()だけであることから、DecodeDepthNormal()を参考にDecodeFloatRG()をそのまま持ってきて解決しています。
また、前述した様に法線がView空間の物になっているので、行列乗算してWorld空間の法線に変換しています。
この変換処理を省くと、カメラが動くたびに法線の向きが変わってしまいます。気になる方はコメントアウトして確認してみると良いかと。出力結果
Blend = 0 の場合 元画像
Blend = 1、DepthOrNormal = 0 の場合 深度を表示
Blend=1、DepthOrNormal = 1 の場合 World法線を表示
という事で、深度と法線の表示が達成可能となります。追記
Z軸を反転させたい場合は↓の記事などを参考に反転させるとよいかと
【Unity】【シェーダ】ビュー空間の法線を_CameraDepthNormalsTextureから取得する - LIGHT11参考資料
Postprocessing with Normal Texture | Ronja's tutorials
William Chyr | Unity Shaders – Depth and Normal Textures (Part 3)