Unityのシェーダでビュー空間の法線を取得する方法をまとめました。
Unity2019.2.10
_CameraDepthNormalsTextureを生成する
Unityのシェーダではビュー空間の法線は_CameraDepthNormalsTextureから取得できます。
このテクスチャを生成するためには、まず以下のようにCameraのdepthTextureModeを変更しておきます。
using UnityEngine; public class DepthNormalTexture : MonoBehaviour { [SerializeField] private Shader _shader; private Material _material; void Start () { // _CameraDepthNormalsTextureを生成するようにする設定 GetComponent<Camera>().depthTextureMode |= DepthTextureMode.DepthNormals; _material = new Material(_shader); } private void OnRenderImage(RenderTexture source, RenderTexture dest) { Graphics.Blit(source, dest, _material); } }
このテクスチャにはビュー空間の法線と深度値が独自のフォーマットで格納されています。
なお上記のソースコードではこのついでに次節のシェーダをポストエフェクトとして適用できるようにしています。
ポストエフェクトの掛け方に関しては下の記事にまとめていますので必要に応じて参照してください。
シェーダで法線を取得する
次にこのテクスチャから、シェーダを使って法線を取得します。
まずシェーダ全文です。
Shader "DepthNormalTexture" { SubShader { Cull Off ZTest Always ZWrite Off Tags{ "RenderType" = "Opaque" } Pass { HLSLPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; }; sampler2D _CameraDepthNormalsTexture; v2f vert(appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = v.uv; return o; } fixed4 frag(v2f i) : SV_Target { // _CameraDepthNormalsTextureからサンプリング float4 cdn = tex2D(_CameraDepthNormalsTexture, i.uv); // サンプリングした値をDecodeViewNormalStereo()で変換 float3 n = DecodeViewNormalStereo(cdn) * float3(1.0, 1.0, -1.0); return float4(n, 1.0); } ENDHLSL } } }
コメントの通り、_CameraDepthNormalsTextureから法線を取得するにはまずこのテクスチャから値をサンプリングし、
サンプリングした値をDecodeViewNormalStereo()
関数で変換します。
またこのままだとZ軸の手前方向が正の値となっているためZ値を反転させています。
レンダリング結果
このシェーダを使ってレンダリングすると以下のような結果が得られます。
正常にビュー空間の法線が得られていそうです。