Unity Shader 物体外轮廓 描边

效果:绘制物体的外轮廓(不是描所有的边,只是描最外围的边),比如LOL中选中塔的效果

Unity Shader 物体外轮廓 描边_第1张图片












       这部分知识在ShaderLab开发实战详解有详细的说明,不过我做了修改,用另一种更简单的方法解决了在不写深度的情况下,描边被遮挡的问题。

       描边原理:要画2遍,第一遍画稍大一号的模型,输出的颜色为描边颜色,大出来的尺寸就是描边的宽度。第二遍再按正常流程画。就好像真实模型外套了一个大一点的套子,因为第一遍没有写深度,所以被套在里面的模型不会被外层的套子遮住。

       计算外面套子的位置有很多种算法,这里就用最简单的一种吧,直接将顶点按照法线的方向移动一点。

       效果图:

       Unity Shader 物体外轮廓 描边_第2张图片

       第一个Pass代码如下:

SubShader
{		
	Tags { "Queue" = "Geometry" "RenderType" = "Opaque"}		
	LOD 200	

	Pass
	{
		Cull Off
		ZWrite Off
		
		CGPROGRAM

			#include "UnityCG.cginc"
			#pragma vertex vert
			#pragma fragment frag

			half _OutlineWidth;
			fixed4 _OutlineColor;

			struct V2F
			{
				float4 pos:SV_POSITION;
			};

			V2F vert(appdata_base IN)
			{
				V2F o;

				IN.vertex.xyz += IN.normal * _OutlineWidth;
				o.pos = mul(UNITY_MATRIX_MVP, IN.vertex);
				return o;
			}

			fixed4 frag(V2F IN):COLOR
			{
				return _OutlineColor;
			}				  	
		ENDCG
	}	

	Pass
	{
		// 正常绘制
	}
}

         看起来很正常,其实有问题,加一个地板看看,效果图:

Unity Shader 物体外轮廓 描边_第3张图片 

     地板会遮挡一部分描边,这是因为非透明物体的绘制顺序为从前往后,既先画怪物描边再画地板,同时描边没有写深度,所以描边被地板挡住,即地板的颜色覆盖了描边的颜色。

     ShaderLab那本书中给出的解决办法是在第一个Pass中剔除正面,同时写深度。    

Cull Front
ZWrite On
     但这会导致所有的边都被描了,虽然描边不会被地板遮挡了,但我们只想描外轮廓的边。如下图:

Unity Shader 物体外轮廓 描边_第4张图片

     到这里,我们的解题思路是:

     1. 只描外轮廓,所以不能写深度。

     2. 不写深度,会导致描边被 地板挡住。

     那现在关键点在于,除了写深度还有哪些方法可以防止描边被地板挡住呢?

     我们只需在画完所有的非透明物体后,再描我们的描边对象就可以了,这样就不会出现被挡住的情况了。这一部的代码如下:

SubShader
{		
    Tags { "Queue" = "Geometry+1"}	
  
   // 省略后面的
}	

Unity Shader 物体外轮廓 描边_第5张图片

     默认的非透明物体的渲染队列为“Geometry","Geometry+1"的意思就是在所有的非透明物体渲染完后再渲染这个队列中的物体。以为这样就没问题了吗?我们放2个使用了描边的怪看看:

Unity Shader 物体外轮廓 描边_第6张图片

      靠,虽然不被地板挡了,但相同队列的物体之间彼此间又会遮挡,同样还是因为没有写深度的问题。再深入一点,前面的怪的描边被后面的怪挡,是因为引擎先画的前面的怪,再画后面的怪导致的,如果先画后面的怪,再画前面的怪那问题就可以解决了。     

Tags { "Queue" = "Transparent" }	
     Unity Shader 物体外轮廓 描边_第7张图片

     终于是我们想要的效果了,使用"Transparent"队列会导致消耗额外的性能,当成透明的物体来渲染,那些被遮挡住的面也会被绘制一遍,做无用功,还会影响批渲染数,不过使用了骨骼动画的物体本来就不能被批渲染,哈哈。

你可能感兴趣的:(Unity,Shader)