抓手
项目组需要一个类似如下的半透明剪影效果,不过我们是3D模型,那么就需要一些特殊的做法。
最终通过使用Stencil Buffer解决了。
详解
一、刚开始的的自以为是
刚做这个需求之前,以为很容易做,不就是在frag shader里return一个半透明的黑色颜色值吗?
然后Tags改成透明
Tags { "RenderType"="Transparent" "Queue" = "Transparent" }
混合模式为
Blend SrcAlpha OneMinusSrcAlpha
Zwrite Off
不就完了吗?
结果说明我真是想得太简单。得到了如下结果:
衣服和身体,重叠了,因为 Blend SrcAlpha OneMinusSrcAlpha的混合计算方式如下:
最终颜色 = shader计算的颜色值*alpha值+(1-alpha值)*屏幕现有颜色值
那么模型身体的像素点渲染过后,覆盖在身体前的衣服再渲染一遍,混合的颜色就成上面那样了。
那这个方案不可行,不是一个严格的剪影。
二、具体的实现办法
有两个办法可以解决上面的问题,
1是使用深度写入:
Zwrite On
ZTest Less
offset -10000000, 0
offset r,m 的r值越小,就只渲染离摄像机越近的像素。
但是这样会导致它会渲染在所有物体之前,会穿UI。这个方案有瑕疵。
2是使用Stencil Buffer
Stencil
{
Ref 0
Comp Equal
Pass Invert
}
这里的Ref值是0,如果buffer里的值也是0的话,就通过stencil test,如果通过stencil test和depth test,
这里写入的模版值是用的Invert状态,也就是255,并渲染该像素。
然后下个像素判断当前buffer值不是0的话,则不通过stencil test,则不会渲染。这样就会得到正确的剪影效果。
这个方案比较完美,既不会渲染到最前面,而且还整得跟纸片一样。
全部的代码如下(以下代码只能在URP管线下使用):
Shader "Custom/CharacterShadow"
{
Properties
{
_Color ("主颜色", Color) = (0,0,0,0.6)
}
SubShader
{
Tags { "RenderType"="Transparent" "Queue" = "Transparent+100" }
LOD 200
Pass
{
Blend SrcAlpha OneMinusSrcAlpha
Zwrite Off
//ZTest Less
//offset -10000000, 0
Stencil
{
Ref 0
Comp Equal
Pass Invert
}
Tags
{
"LightMode" = "UniversalForward"
}
HLSLPROGRAM
#pragma vertex LitPassVertex
#pragma fragment LitPassFragment
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
struct Attributes
{
float4 positionOS : POSITION;
};
struct Varyings
{
float4 pos : SV_POSITION;
};
CBUFFER_START(UnityPerMaterial)
half4 _Color;
CBUFFER_END
Varyings LitPassVertex (Attributes v)
{
Varyings o;
VertexPositionInputs vertexInput = GetVertexPositionInputs(v.positionOS.xyz);
o.pos = vertexInput.positionCS;
return o;
}
half4 LitPassFragment (Varyings i) : SV_Target
{
return _Color;
}
ENDHLSL
}
}
}
最终的效果,完美!