一、2D横版动作游戏升级成3D模型,会出现一个问题,就是站在同一个位置的2个物体会出现模型穿帮,原因是因为2D横版动作游戏是没有物体之间的物理碰撞。要解决这个问题,可以考虑使用模板测试。
二、模板测试使用语法:
Stencil
{
Ref referenceValue
ReadMask readMask
WriteMask writeMask
Comp comparisonFunction
Pass stencilOperation
Fail stencilOperation
ZFail stencilOperation
}
1> Ref referenceValue:用来与缓存中已经存在的模板值进行比较的数值,数值范围0~255
2> ReadMask readMask:是一个范围为0~255的整数,8位二进制11111111,用于指定哪些位从模板缓存中读取。
3> WriteMask writeMask:同样也是8位二进制11111111,用于指定哪些位的数值运行写入缓存。
4> Comp comparisonFunction:将参照组与缓存中的值进行比较的方法,默认是always。
5> Pass comparisonFunction:如果模板和深度测试都通过,缓存中的模板值如何处理,默认是keep。
6> Fail comparisonFunction:如果模板测试不通过,缓存中的模板值如何处理,默认是keep。
7> ZFail comparisonFunction: 如果深度测试不通过,缓存中的模板值如何处理,默认是keep。
三、解决模型穿帮的实现原理:
1> 将场景背景放到"Queue"="Background"绘制;
2> 对于场景中的物体(角色、怪物、可破坏物件、阻挡物件等)开启模板测试,关闭深度测试Zwrite Off,用于模板测试的模板值由逻辑层控制,越前面的越大;
3> 将前景最后绘制。
四、Shader代码如下:
Shader "Unlit/Stencil"
{
Properties
{
_MainTex ("Main Texture", 2D) = "white" {}
_SpecularTex("Specular Texture", 2D) = "white" {}
_Shininess("Shininess", Float) = 20
_Stencil("Stencil ID", Float) = 0
}
SubShader
{
Tags { "RenderType"="Opaque" "Queue"="Geometry" }
LOD 100
Pass
{
Stencil
{
Ref [_Stencil]
Comp Greater
Pass Replace
}
Tags { "LightModel" = "ForwardBase" }
ZWrite Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"
struct v2f
{
float4 pos : SV_POSITION;
float3 normal : NORMAL;
float4 vertex : TEXCOORD0;
float2 main_uv : TEXCOORD1;
float2 specular_uv : TEXCOORD2;
SHADOW_COORDS(3)
};
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _SpecularTex;
float4 _SpecularTex_ST;
float _Shininess;
v2f vert (appdata_base v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.normal = v.normal;
o.vertex = v.vertex;
o.main_uv = TRANSFORM_TEX(v.texcoord, _MainTex);
o.specular_uv = TRANSFORM_TEX(v.texcoord, _SpecularTex);
TRANSFER_SHADOW(o);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
//diffuse
float3 normal = normalize(UnityObjectToWorldNormal(i.normal));
float3 l = WorldSpaceLightDir(i.vertex);
float ndotl = saturate(dot(normal, l));
fixed4 diffuse = _LightColor0 * tex2D(_MainTex, i.main_uv) * ndotl;
//specular
float3 v = normalize(WorldSpaceViewDir(i.vertex));
float3 h = normalize(l + v);
float ndoth = saturate(dot(normal, h));
fixed4 specular = _LightColor0 * tex2D(_SpecularTex, i.specular_uv) * pow(ndoth, _Shininess);
fixed4 color = diffuse + specular + unity_AmbientSky;
UNITY_LIGHT_ATTENUATION(colormask, i, mul(unity_ObjectToWorld, i.vertex).xyz);
color = color * colormask;
return color;
}
ENDCG
}
}
FallBack "Diffuse"
}