最近有個特殊需求是,在做PostEffect時,希望能把一些東西排除掉,列如背景去飽和了,但人物想不被受影響。
但這個在Deferred rendering下會有問題,因為Unity的Deferred Rendering的PostEffect只能作用在最後一隻畫的Camera上,因此想了幾個方法。
1.畫人物時同時畫Stencil,PostEffect判斷Stencil,把人物排除。這個缺點是邊緣會有些鋸齒,Stencil應該沒AA。而且半透明物件就沒辦法。
2.人物在PostEffect之後用Forward畫,。缺點是,Camera深度沒共用,人不會被場景遮蔽。
3.人物用Shader tag,用一隻Camera RenderwithShader,輸出mask。但RenderWithShader同上,只能在Forward下使用,Mask也沒深度。而且另外做mask,drawcall也會增加。
4.把場景、特效、人物,用不用的RenderTexture輸出,最後在另一個PostEffect合併。這是個可行的方法,會增加一些Blit的drawcall,但是固定的。只是會用掉很多Vram。一張Full HD的rendertarget還滿大的。
上述方法好像都有缺點。
如果不同render path的Camera能Share Depth buffer就好了。
所以,如果拿Deferred Rendering的Camera深度,寫到Forward那隻Camera不就解決了?
於是2的Modify選項就出現了。
Camera結構如下:
MainCamera <-- Deferred Rendering
-PlayerCamera <- Forward Rendering (clear flag -> don't clear)
在PlayerCamera掛上一個DepthWrite的Script
Cull Mask設定一下
using UnityEngine; using System.Collections; [ExecuteInEditMode] [RequireComponent(typeof(Camera))] public class DetphWrite : MonoBehaviour { private Material DepthWriteMaterial = null; // Use this for initialization void Start () { DepthWriteMaterial = new Material(Shader.Find("Hidden/DepthWrite")); } void OnPreRender() { DrawQuad(); } void DrawQuad() { GL.PushMatrix(); GL.LoadOrtho(); DepthWriteMaterial.SetPass(0); //Render the full screen quad manually. GL.Begin(GL.QUADS); GL.TexCoord2(0.0f, 0.0f); GL.Vertex3(0.0f, 0.0f, 0.1f); GL.TexCoord2(1.0f, 0.0f); GL.Vertex3(1.0f, 0.0f, 0.1f); GL.TexCoord2(1.0f, 1.0f); GL.Vertex3(1.0f, 1.0f, 0.1f); GL.TexCoord2(0.0f, 1.0f); GL.Vertex3(0.0f, 1.0f, 0.1f); GL.End(); GL.PopMatrix(); } }
Shader如下:
Shader "Hidden/DepthWrite" { Properties { _MainTex ("Base (RGB)", 2D) = "black" {} } SubShader { Pass { ZTest Always Cull Off ZWrite On Fog { Mode off } CGPROGRAM #pragma exclude_renderers gles flash #pragma vertex vert #pragma fragment frag #pragma target 4.0 #include "UnityCG.cginc" // vertex input: position, UV struct appdata { float4 vertex : POSITION; float2 texcoord : TEXCOORD0; }; struct v2f { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; }; v2f vert (appdata v) { v2f o; o.pos = mul( UNITY_MATRIX_MVP, v.vertex ); o.uv = v.texcoord.xy; return o; } sampler2D _MainTex; sampler2D _CameraDepthTexture; struct fragOut { // half4 color : COLOR; don't need float depth : DEPTH; }; fragOut frag( v2f i ) { fragOut o; float depth =UNITY_SAMPLE_DEPTH(tex2D(_CameraDepthTexture,i.uv)); // o.color= 0; // o.depth= depth; return o; } ENDCG } } }本來以為_CameraDepthTexture要自己copy,但發現竟然Get到的是對的,值沒被Clear掉。如果Forward那支Camera,有下Clear flag Depth,就會被清掉。所以如果要自己畫Mask,前面3的方式,應該也可以傳進來自己判斷。
這個方法的缺點是用到Shader Model 4.0,要把Dx11的flag打開。
另外,在PlayerCamera畫的Soft Particle會失效(明明 _CameraDepthTexture抓得到),我把Particle Shader中#ifdef SOFTPARTICLES_ON 註解掉就可以。應該是Unity判斷Camera不同Render Path時,就 define off了。
如果要在d3d9下可以用,另一個想到的方法是在vertex shader讀入depth map,然後事前產生一個plane,它的點數和螢幕pixel一樣多,再寫入depth。可能對點的位置會有點麻煩,但理論上可行。
沒有留言 :
張貼留言