王者荣耀角色描边

角色描边浅析

Posted by Bob on October 27, 2018

描边就是做出物体边缘的线条,通俗讲就是在边缘加上边框,一般是为了把物体和背景区分出来,便于识别。

王者荣耀设置面板有角色描边开关,如果是低端机器开启会卡,为什么?

image

实现方式一

一个pass 所有顶点沿着法线方向扩展一个标量_OutLinePower,绘制纯色_OutLineColor ,另一个pass绘制完整模型

image


Shader "S_Game_Hero/OutLine"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
		_OutLinePower("factor",Range(0,0.1)) = 0.01
        _OutLineColor("outline color",Color) = (0,0,0,1)
	}
	SubShader
	{
		Tags { "RenderType"="Opaque" }
		LOD 100


		Pass
		{
            //绘制一个方向就够了

			Cull Front

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			// make fog work
			#pragma multi_compile_fog

			#include "UnityCG.cginc"

			struct appdata
			{
				float4 vertex : POSITION;
				float3 normal : NORMAL;
			};

			struct v2f
			{
				float4 vertex : SV_POSITION;
			};


			float _OutLinePower;
            half4 _OutLineColor;

			v2f vert (appdata v)
			{
				v2f o;

                //法线方向扩张

				float3 offset = v.normal * _OutLinePower;
				o.vertex = UnityObjectToClipPos( v.vertex + offset);

				return o;
			}

			fixed4 frag (v2f i) : SV_Target
			{
                //绘制纯色
				
				return _OutLineColor;
			}
			ENDCG
		}


		Pass
		{

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			// make fog work
			#pragma multi_compile_fog

			#include "UnityCG.cginc"

			struct appdata
			{
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
			};

			struct v2f
			{
				float2 uv : TEXCOORD0;
				float4 vertex : SV_POSITION;
			};

			sampler2D _MainTex;
			float4 _MainTex_ST;



			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				return o;
			}

			fixed4 frag (v2f i) : SV_Target
			{
				fixed4 col = tex2D(_MainTex, i.uv);
				return col;
			}
			ENDCG
		}
	}
}


这种实现方式有点瑕疵,不仅会造成近大远小的透视问题,并且还出现一些多余的黑面片。

image

有些三角面是正面,有些三角面是反面,用cull Front会剔除正面,保留反面。所以会留下这种黑面片。默认是开启了写入深度缓存zwrite。这些多余的黑面片恰好又在前面,正常的颜色绘制时就通不过深度测试ztest。

image

如果在第一个pass关闭深度缓存:ZWrite Off ,发现多余的黑面片没了,但是又缺失了一些描边。

image

在第一个pass中再特殊处理一下这些黑面的z轴。


  v2f vert (appdata v)
{
	v2f o;

	//float3 offset = v.normal * _OutLinePower;  

	//o.vertex = UnityObjectToClipPos( v.vertex + offset);



	 //将顶点转化到观察空间中

	 float4 pos = mul( UNITY_MATRIX_MV, v.vertex);

	 //将法向转化到观察空间中

	 //如果用UNITY_MATRIX_MV不用UNITY_MATRIX_IT_MV,将不会再垂直原来的面了

	 float3 normal = mul( (float3x3)UNITY_MATRIX_IT_MV, v.normal);
	 //会把太靠前的黑面反向拉一下

	 normal.z -= 3;

	 pos = pos + float4(normalize(normal),0) * _OutLinePower;
	 
	 //将顶点转化到投影空间中
	 
	 o.vertex = mul(UNITY_MATRIX_P, pos);


	return o;
}