UnityURP 使用StencilBuffer制作一个“看见看不见”的球
UnityURP 使用StencilBuffer制作一个“看见看不见”的球
- 前言
- 项目
- 创建Shader
- 创建材质
- 搭建场景
- Stencil Buffer 工作原理
- 注意事项
- 遮罩组共享机制
- 鸣谢
前言
相机只有通过特定相框(遮罩)才能看到这个球,其他位置看不见。
在Unity中使用Stencil Buffer来实现对象遮罩效果。
项目
创建Shader
创建遮罩Shader
StencilMask.shader
Shader "Custom/StencilMask"
{Properties{_StencilID ("Stencil ID", Int) = 1}SubShader{Tags { "RenderType"="Opaque" "Queue"="Geometry-100"}ColorMask 0ZWrite offStencil{Ref [_StencilID]Comp alwaysPass replace}Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct appdata{float4 vertex : POSITION;};struct v2f{float4 vertex : SV_POSITION;};v2f vert (appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);return o;}fixed4 frag (v2f i) : SV_Target{return fixed4(0, 0, 0, 0);}ENDCG}}
}
创建被遮罩物体Shader
StencilObject.shader
Shader "Custom/StencilObject"
{Properties{_MainTex ("Texture", 2D) = "white" {}_Color ("Color", Color) = (1,1,1,1)_StencilID ("Stencil ID", Int) = 1[Enum(UnityEngine.Rendering.CompareFunction)] _StencilComp ("Stencil Comparison", Int) = 3}SubShader{Tags { "RenderType"="Opaque" "Queue"="Geometry"}LOD 100Stencil{Ref [_StencilID]Comp [_StencilComp]}Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag#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;fixed4 _Color;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) * _Color;return col;}ENDCG}}
}
创建材质
- 创建两个材质:
MaskMaterial
和StencilObjectMaterial
- 将
StencilMask.shader
应用到MaskMaterial
- 将
StencilObject.shader
应用到StencilObjectMaterial
搭建场景
- 创建遮罩对象(如Cube、Sphere等),并添加
MaskMaterial
材质 - 创建需要被遮罩的对象并添加
StencilObjectMaterial
材质
- 被遮罩的对象只会在遮罩区域内显示
- 可以在运行时动态修改Stencil ID来切换不同的遮罩组
Stencil Buffer 工作原理
- 遮罩阶段:遮罩Shader将指定的Stencil ID写入Stencil Buffer,但不输出颜色
- 渲染阶段:被遮罩对象Shader检查Stencil Buffer,只在匹配的区域渲染
常用Stencil比较函数
Never (0)
- 从不通过Less (1)
- 小于时通过Equal (2)
- 等于时通过LessEqual (3)
- 小于等于时通过Greater (4)
- 大于时通过NotEqual (5)
- 不等于时通过GreaterEqual (6)
- 大于等于时通过Always (7)
- 总是通过
注意事项
- 遮罩对象的渲染队列应该早于被遮罩对象(Queue=“Geometry-100” vs Queue=“Geometry”)
- Stencil Buffer 有8位深度,支持0-255的值
- 相同Stencil ID的对象会共享遮罩区域
遮罩组共享机制
使用相同Stencil ID的对象会形成"联合遮罩区域":
- ID=1的圆形 + ID=1的方形 = 圆形+方形的联合区域
- 被遮罩对象只在这个联合区域内显示
鸣谢
Claude Code