效果:绘制物体的外轮廓(不是描所有的边,只是描最外围的边),比如LOL中选中塔的效果:
这部分知识在ShaderLab开发实战详解有详细的说明,不过我做了修改,用另一种更简单的方法解决了在不写深度的情况下,描边被遮挡的问题。
描边原理:要画2遍,第一遍画稍大一号的模型,输出的颜色为描边颜色,大出来的尺寸就是描边的宽度。第二遍再按正常流程画。就好像真实模型外套了一个大一点的套子,因为第一遍没有写深度,所以被套在里面的模型不会被外层的套子遮住。
计算外面套子的位置有很多种算法,这里就用最简单的一种吧,直接将顶点按照法线的方向移动一点。
效果图:
第一个Pass代码如下:
<span style="font-size:14px;">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
{
// 正常绘制
}
}</span>
看起来很正常,其实有问题,加一个地板看看,效果图:
地板会遮挡一部分描边,这是因为非透明物体的绘制顺序为从前往后,既先画怪物描边再画地板,同时描边没有写深度,所以描边被地板挡住,即地板的颜色覆盖了描边的颜色。
ShaderLab那本书中给出的解决办法是在第一个Pass中剔除正面,同时写深度。
Cull Front
ZWrite On
但这会导致所有的边都被描了,虽然描边不会被地板遮挡了,但我们只想描外轮廓的边。如下图:
到这里,我们的解题思路是:
1. 只描外轮廓,所以不能写深度。
2. 不写深度,会导致描边被 地板挡住。
那现在关键点在于,除了写深度还有哪些方法可以防止描边被地板挡住呢?
我们只需在画完所有的非透明物体后,再描我们的描边对象就可以了,这样就不会出现被挡住的情况了。这一部的代码如下:
SubShader
{
Tags { "Queue" = "Geometry+1"}
// 省略后面的
}
默认的非透明物体的渲染队列为“Geometry","Geometry+1"的意思就是在所有的非透明物体渲染完后再渲染这个队列中的物体。以为这样就没问题了吗?我们放2个使用了描边的怪看看:
靠,虽然不被地板挡了,但相同队列的物体之间彼此间又会遮挡,同样还是因为没有写深度的问题。再深入一点,前面的怪的描边被后面的怪挡,是因为引擎先画的前面的怪,再画后面的怪导致的,如果先画后面的怪,再画前面的怪那问题就可以解决了。
Tags { "Queue" = "Transparent" }
终于是我们想要的效果了,使用"Transparent"队列会导致消耗额外的性能,当成透明的物体来渲染,那些被遮挡住的面也会被绘制一遍,做无用功,还会影响批渲染数,不过使用了骨骼动画的物体本来就不能被批渲染,哈哈。