Unity开发到一定阶段,Shader是一个不得不接触的东西,很多东西也确确实实只能用Shader去解决,C#是解决不了的。虽然这两年绝地求生是热度有些下滑,但还是有很多职业选手参与比赛,这其中就有我们熟知的韦神之类的。但在观看赛事时,我们从解说的视角来看,似乎所有的人,我们都能看到,想开了透视外挂,只是在障碍物后的人物渲染的方式有些不同,那这种被遮挡后还能看到,且换了一种渲染方式的功能是怎么去实现的呢?接下来我们就来详细看看,其实没那么复杂。
首先,我们先要了解Shader中通道(Pass)的概念,通道指的是画面渲染的最终执行内容,一般情况下,Shader是使用单通道的,但如果遇到渲染是需要条件限制,或特殊渲染,此时则需要多通道。当然渲染通道越多,渲染所消耗的性能越大。
实现遮挡特效的原理很简单,只需要判断角色是否被其他物体所遮挡,如果没有被遮挡了,则用通道1去渲染;如果被遮挡了就用通道2去渲染。那么问题来了,如何判断角色被遮挡了呢?
在ShaderLab中有深度测试的命令(ZTest),可以测试当前顶点在渲染时的深度,当然测试之后就可以产生对比,在ZTest后面可以添加对比的关键词,如大于(Greater),小于(Less),大于等于(GEqual),小于等于(LEqual)等。
也就是说如果ZTest Greater,那么说明当前对象的深度更大,也就是距离摄像机更远,即被遮挡,反之ZTest LEqual说明当前深度小,没被遮挡。当然这里非常重要的一点是,先要判断ZTest Greater的时候,要关闭深度缓存ZWrite,从而保证后面的Pass可以覆盖当前Pass的渲染。具体代码如下:
SubShader { ZWrite Off //先判断Greater,因此此Pass必须放在第一个 Pass { ZTest Greater Color(1,0,0,1) } Pass { ZTest LEqual Color(0,1,0,1) } }
此时,我们看到方块在正常渲染的时候渲染为绿色,当被遮挡的时候,渲染成红色,从而达成遮挡渲染,即透视。当然,上例,使用的固定管线渲染,灵活度比较差,接下来我们来看顶点片段的代码:
Shader "Custom/Demo" { Properties { _MainTex ("Main Tex", 2D) = "white" {} _OcclusionColor("OcclusionColor",Color)=(0.5,1,0.5,1) } SubShader { Tags { "RenderType"="Opaque" } LOD 200 //先判断Greater,因此此Pass必须放在第一个 Pass { ZTest Greater ZWrite Off CGPROGRAM #pragma vertex vert #pragma fragment frag fixed4 _OcclusionColor; float4 vert(float4 v : POSITION) : SV_POSITION { return UnityObjectToClipPos(v); } fixed4 frag() : SV_Target{ return _OcclusionColor; } ENDCG } Pass { ZTest LEqual Tags { "LightMode"="ForwardBase" } CGPROGRAM #pragma vertex vert #pragma fragment frag #include "Lighting.cginc" sampler2D _MainTex; float4 _MainTex_ST; struct a2v { float4 vertex : POSITION; float4 texcoord : TEXCOORD0; }; struct v2f { float4 position : SV_POSITION; float2 uv : TEXCOORD0; }; v2f vert(a2v v) { v2f o; o.position = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); return o; } fixed4 frag(v2f i) : SV_Target { fixed4 c = tex2D(_MainTex, i.uv); return fixed4(c.rgb, 1.0); } ENDCG } } FallBack "Diffuse" }
到此,我们就完成了遮挡特效的Shader,顶点片段层面下,大家可以设置自己想要的渲染效果,法线、高光、边缘光等。包括遮挡时,也可以设置稍微酷炫一些的特效。小伙伴们,你们学会了吗?