在游戏中,为了表现道具的选中效果,通常会在被选中的物体添加一圈描边效果。
那么如何通过Shader实现物体的描边效果呢?
1.0实现原理
描边Shader有多种实现方式,可以通过后期处理实现,也可以通过MatCap实现。本案例通过俩个Pass实现的。
- 第一个Pass,将模型的顶点位置沿着法线膨胀一段距离,然后再为膨胀之后的模型指定一个纯色进行着色,着色不需参与任何灯光交互。
- 第二个Pass,模型以正常效果进行显示。
在正常情况下,第一个Pass膨胀之后的模型会将第二个Pass模型完全笼罩起来,因此第二个Pass模型无法通过深度测试,最终只会显示第一个Paas之后的效果。为了解决这个问题,需想办法让第二个Pass模型通过深度测试。方法有如下俩种:
- 更改第二个Pass的深度值比较方法(如,Always,Greater),但是这样会导致其他物体进行深度测试时出现错误,因此排除该方法。
- 关闭第一个Pass的深度写入,如此一来,深度缓存中没有第一个Pass模型的深度值,第二个Pass模型自然就可以通过深度测试了。
因此,最终选择第二种方法。但是这又会引发另外一个问题,当物体关闭深度写入之后,后面被遮挡的物体就无法知道自己被遮挡住,本该测试失败的情况,现在反而通过了深度测试,因此绘制图像的时候就会遮挡掉已经绘制好的第一个Pass模型。
为了避免出现这个问题,需要使该模型在所有不透明物体绘制完成之后再进行绘制,因此更改Shader渲染队列为Transparent,如此以来绘制的图像就不会被后面的物体覆盖了。
2.0实现逻辑
Shader代码如下:
Shader "Samples/Outline"
{
Properties
{
[Header(Texture Group)]
[Space(10)]
_Albedo ("Albedo", 2D) = "white" {}
[NoScaleOffset]_Specular ("Specular (RGB-A)", 2D) = "black" {}
[NoScaleOffset]_Normal ("Normal", 2D) = "bump" {}
[NoScaleOffset]_AO ("Ambient Occlusion", 2D) = "white" {}
[Header(Outline Properties)]
[Space(10)]
_OutlineColor ("Outline Color", Color) = (1,0,1,1)
_OutlineWidth ("Outline Width", Range(0, 0.1)) = 0.01
}
SubShader
{
// 本Shader属于不透明效果,因此将渲染类型设置为Opaque
// 为了使物体在所有不透明物体之后在进行绘制,需要将渲染队列设置为"Transparent"
Tags { "RenderType"="Opaque" "Queue" = "Transparent"}
//---------- 描边效果 ----------
Pass
{
// 为了不遮挡住后面的Pass,需要将深度写入关闭
ZWrite Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct v2f
{
float4 vertex : SV_POSITION;
};
fixed4 _OutlineColor;
fixed _OutlineWidth;
v2f vert(appdata_base v)
{
v2f o;
v.vertex.xyz += v.normal * _OutlineWidth;
o.vertex = UnityObjectToClipPos(v.vertex);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
return _OutlineColor;
}
ENDCG
}
//---------- 正常Shader效果 ----------
CGPROGRAM
// 添加fullforwardshadows指令,是物体支持所有灯光类型的投影
#pragma surface surf StandardSpecular fullforwardshadows
struct Input
{
float2 uv_Albedo;
};
sampler2D _Albedo;
sampler2D _Specular;
sampler2D _Normal;
sampler2D _AO;
void surf (Input IN, inout SurfaceOutputStandardSpecular o)
{
fixed4 c = tex2D (_Albedo, IN.uv_Albedo);
o.Albedo = c.rgb;
fixed4 specular = tex2D (_Specular, IN.uv_Albedo);
o.Specular = specular.rgb;
o.Smoothness = specular.a;
o.Normal = UnpackNormal(tex2D (_Normal, IN.uv_Albedo));
o.Occlusion = tex2D (_AO, IN.uv_Albedo);
}
ENDCG
}
}
效果如下:

3.0 描边算法缺陷
该描边算法只适合顶点法线方向各异的模型(如球),而对于一些顶点法线方向比较一致的模型(如立方体)会出现如下缺陷。

网友评论