这个还是挺久之前做的,都快忘了。上篇总结后处理的时候在乱七八糟的工程里突然找到,就也来写一篇。
效果还是挺有意思的,我觉得放在人物选择界面这种会很棒。
unity的渲染管线的逐片元操作阶段有三项测试,通过三项测试的片元(fragment)才能成为像素(pixel)。
虽然这个阶段是不可编程的,但是却是高度可配置的。
三项测试按顺序分别是:
透明度测试(Alpha Test)
低于给定透明度的片元会被舍弃,默认是关闭的。
模板测试(Stencil Test)。
如果开启该测试,会在模板测试阶段读取片元的模板值,并与给定的参考值进行比较,再决定是否舍弃该片元。
一般用来限制渲染区域,可用来渲染轮廓、阴影等用法。
默认是关闭的。
深度测试(Depth Test)
读取片元的深度值,并与深度缓冲区已存在的深度值进行比较。
如果片元深度值大于缓冲器深度值,则说明该片元在场景中已有物体之后,则舍弃该片元;反之则说明该片元在场景已有物体之前,则保留该片元,并把缓冲区深度值更新为该片元深度值。
默认开启,可配置深度测试和深度写入的开启与否来渲染半透明物体(要很小心渲染顺序)。
源码
模板测试可以做出很多很有意思的东西,比如上面那个转转乐。具体的模板测试各种操作配置可以见这篇博文
转转乐的具体思路是在方框的四个侧面分别放四个plane当做mask,在外部写入模板值,并设置只有相同模板值才能渲染。
虽然写了阴影的模板测试,但是不知道为啥会出现比较诡异的阴影情况,模板配置改了很多遍也没法正常,只能关掉阴影投射和接收,希望以后哪一天我搞定了再回来改掉。
mask和几何体的shader源码如下:
StencilMask源码
Shader "Unlit/StencilMask"
{
Properties
{
_StencilMask("Stencil Mask", Int) = 0
}
SubShader
{
Tags{ "RenderType" = "Opaque" "Queue" = "Geometry" }
ColorMask 0 //不输出任何颜色
ZWrite off //关闭深度写入
Stencil
{
Ref[_StencilMask]
Comp Always
Pass Replace
}
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
struct appdata
{
float4 vertex : POSITION;
};
struct v2f
{
float4 pos : SV_POSITION;
};
v2f vert(appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
return o;
}
half4 frag(v2f i) : COLOR
{
return half4(1,1,1,1);
}
ENDCG
}
}
}
StencilGeometry源码
Shader "Unlit/StencilGeometry"
{
Properties
{
_StencilMask("Stencil Mask", Int) = 0
_Color("Color", Color) = (1,1,1,1)
}
SubShader
{
Tags{ "RenderType" = "Opaque" "Queue" = "Geometry+1" }
Pass
{
Stencil
{
Ref[_StencilMask]
Comp equal //只有与模板缓冲区里的值相等才通过模板测试
Pass Keep
}
Tags {"LightMode" = "ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
#include "AutoLight.cginc"
#include "Lighting.cginc"
fixed4 _Color;
struct v2f
{
float4 pos : SV_POSITION;
float4 worldPos : TEXCOORD0;
float3 worldNormal : TEXCOORD1;
SHADOW_COORDS(2)
};
v2f vert(appdata_base v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
TRANSFER_SHADOW(o);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * _Color.rgb;
fixed3 diffuse = _LightColor0.rgb * _Color.rgb * saturate(dot(worldNormal, worldLightDir));
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
return fixed4(diffuse * atten + ambient, 1.0);
}
ENDCG
}
Pass
{
Stencil
{
Ref 0
Comp equal
Pass Keep
}
Tags {"LightMode" = "ShadowCaster"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_shadowcaster
#include "UnityCG.cginc"
struct v2f
{
V2F_SHADOW_CASTER;
};
v2f vert(appdata_base v)
{
v2f o;
TRANSFER_SHADOW_CASTER_NORMALOFFSET(o);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
SHADOW_CASTER_FRAGMENT(i);
}
ENDCG
}
}
FallBack "Diffuse"
}