Unity提供了不少Image Effect效果,包含Global Fog、DOF、Boom、Blur、Edge Detection等等,這些效果裏面都會使用到攝像機深度或者根據深度還原世界座標實現各類效果,這篇文章主要介紹Unity中獲取相機深度的方式。html
Image Effect是Post Effect中的一種方式,Camera GameObject腳本上掛在腳本帶有OnImageRender來實現, 具體實現參考Unity官網說明.
對於深度紋理,相機需設置DepthTextureMode參數,可設置爲DepthTextureMode.Depth或者DepthTextureMode.DepthNormals,配合unity shaderLab中提供的參數_CameraDepthTexture 或者_CameraDepthNormalsTexture來獲取。git
深度紋理並不是深度緩衝中的數據,而是經過特定Pass得到。github
Unity4.X和Unity5.X版本的實現方式不太同樣,Unity4.X經過"RenderType" 標籤經過Camera Shader 替換獲取,Unity5經過ShadowCaster Pass獲取,Unity5官方文檔描述:app
Depth texture is rendered using the same shader passes as used for shadow caster rendering (ShadowCaster pass type). So by extension, if a shader does not support shadow casting (i.e. there’s no shadow caster pass in the shader or any of the fallbacks), then objects using that shader will not show up in the depth texture.
Make your shader fallback to some other shader that has a shadow casting pass, or If you’re using surface shaders, adding an addshadow directive will make them generate a shadow pass too.
Note that only 「opaque」 objects (that which have their materials and shaders setup to use render queue <= 2500) are rendered into the depth texture.ide
對於自身帶有ShadowCaster Pass或者FallBack中含有,而且Render Queue小於等於2500的渲染對象纔會出如今深度紋理中,詳細的測試能夠參考:【Unity Shader】Shadow Caster、RenderType和_CameraDepthTexture學習
在Shader中,需提早定義紋理_CameraDepthTexture ,爲了兼容不一樣的平臺,Unity ShaderLab提供了UNITY_SAMPLE_DEPTH、SAMPLE_DEPTH_TEXTURE方法解決這個問題。測試
Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" //提早定義 uniform sampler2D_float _CameraDepthTexture; struct uinput { float4 pos : POSITION; }; struct uoutput { float4 pos : SV_POSITION; }; uoutput vert(uinput i) { uoutput o; o.pos = mul(UNITY_MATRIX_MVP, i.pos); return o; } fixed4 frag(uoutput o) : COLOR { //兼容問題 float depth = UNITY_SAMPLE_DEPTH(tex2D(_CameraDepthTexture, o.uv)); depth = Linear01Depth(depth) * 10.0f; return fixed4(depth,depth,depth,1); } ENDCG
帶有Normals和depth 的的32位貼圖,Normals根據Stereographic projection編碼到R&G通道,Depth經過映射編碼到 B&A 通道。Unity ShaderLab也提供DecodeDepthNormal 方法驚醒解碼,其中深度是0~1範圍。優化
Normals & Depth Texture 是經過Camera Shader replacement實現,能夠將RenderType爲:Opaque、TransparentCutout、TreeBark、TreeLeaf、TreeOpaque、TreeTransparentCutout、TreeBillboard、GrassBillboard、Grass類型纔會進行深度渲染,對於Transparent\AlphaTest是不會渲染到這個紋理中。詳情可參考:淺析Unity shader中RenderType的做用及_CameraDepthNormalsTextureui
在使用中,需提早定義_CameraDepthNormalsTexture,使用DecodeDepthNormal解碼,須要注意的是:深度是0~1範圍,和_CameraDepthTexture 有區別。編碼
對於透明物體,上面兩種方式都不能很好的得到深度紋理,好比說在以水爲主的場景中,這兩種方式得到的結果都不是太理想(嘗試 unity自帶水的例子)。 這種狀況下,能夠經過相似_CameraDepthNormalsTexture實現方式,自定義深度sheader,經過Camera Shader replacement方式採集深度。
shader部分:
Shader "Custom/CopyDepth" { Properties { _MainTex ("", 2D) = "white" {} _Cutoff ("", Float) = 0.5 _Color ("", Color) = (1,1,1,1) } SubShader { Tags { "RenderType"="Transparent" } Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct v2f { float4 pos : SV_POSITION; float4 nz : TEXCOORD0; }; v2f vert( appdata_base v ) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); o.nz.xyz = COMPUTE_VIEW_NORMAL; o.nz.w = COMPUTE_DEPTH_01; return o; } fixed4 _Color; fixed4 frag(v2f i) : SV_Target { //clip(_Color.a-0.01); return EncodeDepthNormal (i.nz.w, i.nz.xyz); } ENDCG } } Fallback Off }
腳本部分:
using UnityEngine; using System.Collections; [RequireComponent(typeof (Camera))] public class CustomDepth : MonoBehaviour { public GameObject depthCamObj; private Camera mCam; private Shader mCustomDepth; private Material mMat; private RenderTexture depthTexture; private Shader mCopyShader ; void Awake() { mCam = GetComponent<Camera>(); mCustomDepth = Shader.Find("Custom/CustomDepth"); mCopyShader = Shader.Find("Custom/CopyDepth"); mMat = new Material(mCustomDepth); // mCam.SetReplacementShader(Shader.Find("Custom/CopyDepth"), "RenderType"); } //可優化 internal void OnPreRender() { if (depthTexture) { RenderTexture.ReleaseTemporary(depthTexture); depthTexture = null; } Camera depthCam; if (depthCamObj == null) { depthCamObj = new GameObject("DepthCamera"); depthCamObj.AddComponent<Camera>(); depthCam = depthCamObj.GetComponent<Camera>(); depthCam.enabled = false; // depthCamObj.hideFlags = HideFlags.HideAndDontSave; } else { depthCam = depthCamObj.GetComponent<Camera>(); } depthCam.CopyFrom(mCam); depthTexture = RenderTexture.GetTemporary(mCam.pixelWidth, mCam.pixelHeight, 16, RenderTextureFormat.ARGB32); depthCam.backgroundColor = new Color(0, 0, 0, 0); depthCam.clearFlags = CameraClearFlags.SolidColor; ; depthCam.targetTexture = depthTexture; depthCam.RenderWithShader(mCopyShader, "RenderType"); mMat.SetTexture("_DepthTexture", depthTexture); } void OnRenderImage(RenderTexture source, RenderTexture destination) { if (null != mMat) { Graphics.Blit(source, destination, mMat); } else { Graphics.Blit(source, destination); } } }
Shader中採集了Normals & Depth Texture ,固然也能夠只寫Depth Texture。其中只實現了RenderType=Transparent的類型,固然也能夠添加其餘RenderType類型,具體能夠參考Unity Shader源碼文件「Hidden/Camera-DepthNormalTexture」。腳本只是測試使用,具體還需優化。
固然也能夠在本身的shader中單獨定義一個Pass來得到深度紋理,不一樣再去依賴Unity自身的實現方式(避免Unity升級的各類蛋疼)。
使用中,ShaderLab也提供一些其餘方法:Linear01Depth() 、LinearEyeDepth()等(可參考Unity源碼UnityCG.cginc)等。還須要注意在不一樣平臺的差別,能夠參考官方說明中關於深度的部分:https://docs.unity3d.com/Manual/SL-PlatformDifferences.html
Unity Effect資源包中提供各類各樣的效果,能夠用於學習。
官方文檔
如何採集深度
定製本身的 Depth Texture
相機深度紋理
Unity Shaders – Depth and Normal Textures (Part 3)