当前位置: 首页 > news >正文

Unity Stencil遮罩实战:5分钟搞定物体穿透效果(附完整Shader代码)

Unity Stencil遮罩实战:5分钟搞定物体穿透效果(附完整Shader代码)

在游戏开发中,我们经常需要实现一些特殊的视觉效果,比如让物体部分穿透显示、局部隐藏或只显示特定形状的部分。这些效果如果通过传统建模方式实现,不仅效率低下,而且难以动态调整。Unity的Stencil Buffer技术正是解决这类问题的利器。

Stencil Buffer(模板缓冲)是GPU提供的一种特殊缓冲区,它允许我们基于像素级别的条件来控制渲染结果。与深度缓冲类似,但提供了更灵活的控制方式。通过合理设置Stencil参数,我们可以实现诸如X光透视、物体局部显示、复杂形状遮罩等高级视觉效果。

1. Stencil核心原理与参数解析

Stencil Buffer的工作原理可以类比为"数字剪纸":我们首先定义一张"模板纸"(Stencil Buffer),然后在上面"刻出"特定形状(通过设置Stencil值),最后根据这些"刻痕"来决定哪些像素应该被渲染。

1.1 关键参数详解

Unity中的Stencil设置主要包含以下几个核心参数:

参数名称作用常用值
Ref参考值,用于与Buffer中的值比较0-255整数
Comparison比较函数,决定如何比较Ref和Buffer值Less/Equal/Greater等
Operation比较后如何更新Buffer值Keep/Replace/Increment等
Read/Write Mask读写时的位掩码0-255,默认255

**比较函数(Comparison)**决定了像素是否被渲染的规则:

public enum CompareFunction { Disabled = 0, Never = 1, Less = 2, Equal = 3, LEqual = 4, Greater = 5, NotEqual = 6, GEqual = 7, Always = 8 }

**操作(Operation)**决定了比较后如何更新Buffer值:

public enum StencilOp { Keep = 0, Zero = 1, Replace = 2, IncrementSaturate = 3, DecrementSaturate = 4, Invert = 5, IncrementWrap = 6, DecrementWrap = 7 }

提示:Stencil Buffer默认值为0,Read/Write Mask默认255(不进行掩码操作)

1.2 工作流程解析

Stencil测试发生在深度测试之前,其完整工作流程如下:

  1. 读取当前像素位置的Stencil Buffer值
  2. 应用Read Mask进行位运算
  3. 与Ref值进行比较(根据Comparison设置)
  4. 根据比较结果决定是否渲染该像素
  5. 无论是否渲染,都执行Operation更新Buffer值

2. 实战:物体穿透效果实现

现在我们来实现一个典型的穿透效果:当两个物体相交时,只显示相交部分或排除相交部分。

2.1 基础场景设置

首先准备测试场景:

  1. 创建两个3D对象(如Cube和Sphere)
  2. 为每个对象创建独立材质(MatCube和MatSphere)
  3. 调整对象位置使它们部分相交
// 基础Shader结构 Shader "Custom/StencilDemo" { Properties { _Color ("Color", Color) = (1,1,1,1) } SubShader { Tags { "RenderType"="Opaque" } LOD 200 // 这里将添加Stencil配置 // ... } }

2.2 穿透效果Shader配置

实现相交部分显示效果的Shader配置:

// Cube材质Shader的Stencil部分 Stencil { Ref 1 Comp Always Pass Replace } // Sphere材质Shader的Stencil部分 Stencil { Ref 1 Comp Equal Pass Keep }

这种配置会产生以下效果:

  • Cube会将所有渲染区域的Stencil Buffer值设为1
  • Sphere只会在Buffer值为1的区域(即Cube渲染过的区域)显示
  • 最终效果:只显示两个物体的相交部分

2.3 效果变体:排除相交部分

如果想实现相反效果(显示不相交部分),只需修改Sphere的Comparison:

Stencil { Ref 1 Comp NotEqual Pass Keep }

3. 高级应用技巧

掌握了基础用法后,我们可以实现更复杂的效果。

3.1 多层遮罩控制

通过设置不同的Ref值,可以实现多层遮罩效果:

  1. 第一层物体设置Ref=1, Operation=Replace
  2. 第二层物体设置Ref=2, Operation=Replace
  3. 显示物体设置:
    Stencil { Ref 1 Comp Equal Pass Keep }

3.2 动态修改Stencil参数

通过C#脚本可以运行时动态修改Stencil参数:

// 获取材质实例 Material mat = GetComponent<Renderer>().material; // 修改Stencil参数 mat.SetInt("_StencilRef", 2); mat.SetInt("_StencilComp", (int)CompareFunction.Equal); mat.SetInt("_StencilOp", (int)StencilOp.Replace);

注意:直接修改材质属性会创建实例,如需批量修改应考虑使用MaterialPropertyBlock

3.3 与UI系统的结合

Stencil也可用于UI系统的特殊效果,如不规则形状按钮:

// UI遮罩Shader Stencil { Ref 1 Comp Always Pass Replace } // 被遮罩UI元素的Shader Stencil { Ref 1 Comp Equal Pass Keep }

4. 完整Shader代码与优化建议

4.1 完整Shader模板

以下是可直接复用的Stencil效果Shader模板:

Shader "Custom/StencilFilter" { Properties { _MainTex ("Texture", 2D) = "white" {} _Color ("Color", Color) = (1,1,1,1) [Enum(UnityEngine.Rendering.CompareFunction)] _StencilComp ("Stencil Comparison", Int) = 8 [IntRange] _StencilRef ("Stencil Reference", Range(0, 255)) = 0 [Enum(UnityEngine.Rendering.StencilOp)] _StencilOp ("Stencil Operation", Int) = 0 } SubShader { Tags { "Queue"="Geometry" "RenderType"="Opaque" } Stencil { Ref [_StencilRef] Comp [_StencilComp] Pass [_StencilOp] } CGPROGRAM #pragma surface surf Standard fullforwardshadows #pragma target 3.0 struct Input { float2 uv_MainTex; }; sampler2D _MainTex; fixed4 _Color; void surf (Input IN, inout SurfaceOutputStandard o) { fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color; o.Albedo = c.rgb; o.Alpha = c.a; } ENDCG } FallBack "Diffuse" }

4.2 性能优化建议

  1. 渲染顺序控制

    • 先渲染Stencil写入物体,再渲染Stencil测试物体
    • 可通过修改Renderer的materialRenderQueue或Shader的Queue标签调整
  2. 减少Stencil Buffer写入

    • 不需要更新Buffer时使用Operation=Keep
    • 使用Write Mask限制写入范围
  3. 多相机处理

    • 不同相机使用不同的Stencil Buffer
    • 可通过Camera.clearStencilAfterRenderTarget设置
  4. 移动端优化

    • 尽量使用简单的Comparison函数(Equal/Less等)
    • 避免每帧修改Stencil参数

5. 常见问题解决方案

5.1 遮罩效果不显示

可能原因及解决方法:

  1. 渲染顺序错误

    • 确保写入Stencil的物体先渲染
    • 使用Frame Debugger检查实际渲染顺序
  2. Ref值不匹配

    • 检查写入和读取的Ref值是否一致
    • 确认Read/Write Mask设置正确
  3. Shader不支持

    • 确保Shader包含Stencil块
    • 检查Shader编译目标级别(至少Shader Model 3.0)

5.2 与透明物体的交互

处理透明物体时的注意事项:

  1. 透明物体需要额外的渲染队列设置:

    Tags { "Queue"="Transparent" "RenderType"="Transparent" }
  2. 可能需要关闭深度写入:

    ZWrite Off
  3. 混合模式设置:

    Blend SrcAlpha OneMinusSrcAlpha

5.3 多平台兼容性

不同平台的Stencil Buffer行为可能略有差异:

  1. 移动平台

    • 某些低端设备Stencil Buffer精度有限
    • 建议测试Ref值在0-15范围内
  2. WebGL

    • 需要确认WebGL上下文创建时启用了Stencil Buffer
    • 在Unity Player Settings中启用Stencil选项
  3. VR平台

    • 注意单通道立体渲染的影响
    • 可能需要特殊的Shader变体处理
http://www.jsqmd.com/news/568584/

相关文章:

  • C++开发者必看:Deleaker实战教程,轻松解决内存和GDI泄漏问题
  • Qwen3.5-2B低功耗部署:树莓派5+USB GPU加速器运行实测记录
  • WPF布局实战:DockPanel控件在复杂界面设计中的高效应用
  • Linux文件权限管理与实战技巧详解
  • 如何高效管理Steam成就?这款开源工具让游戏数据掌控更简单
  • 图论核心概念辨析:从可行流到完美匹配的20个关键问题
  • 【深度解析】用 Superpowers 改造 AI 编码代理:从“快手实习生”到“有流程的工程师”
  • Arduino老手踩坑实录:ESP32的3个硬件串口和Arduino到底哪里不一样?
  • nlp_structbert_sentence-similarity_chinese-large 赋能智能客服:基于Vue前端的问题相似度匹配实践
  • AI镜像爱好者入门指南:2026年如何系统学习主流大模型
  • Claude Code Pro订阅实战:从零配置到CLI高效编程的完整指南
  • 单片机技术入门与实战:从零基础到项目开发
  • 零门槛体验:AI全身全息感知镜像,上传全身照片自动生成骨骼动画
  • 【技术干货】把 Claude 变成“本地自动化工程师”:Anthropic Computer Use 能力与实战落地指南
  • 【Java记录模式性能黑盒解析】:GraalVM vs HotSpot下模式匹配耗时对比实测,第4种写法竟导致吞吐量腰斩?
  • SMUDebugTool核心功能全解析:从故障排查到性能优化
  • 3步打造高效屏幕标注工作流:教师、程序员与设计师的协作利器
  • 告别重复登录:D2RML如何革新暗黑2重制版多开体验
  • Adafruit Motor Shield V1 驱动原理与嵌入式电机控制实践
  • Cortex-M3任务切换机制与PendSV异常详解
  • OpenClaw vs Cursor vs Claude Code:2026 AI 编程插件实测,哪个真能提效?
  • 别再无脑用LoRA了!从代码生成到持续学习,你的α和rank选对了吗?(附避坑实验)
  • Python 3.14 JIT编译延迟高达83ms?这不是Bug,是设计——揭秘AST→LLVM IR→Native Code三级缓存失效链
  • 3步终极指南:如何让老旧Mac重获新生,免费升级到最新macOS系统
  • 3步解锁FGA自动战斗:告别重复操作,高效管理F/GO日常任务
  • 告别复杂配置!M2FP人体解析服务一键部署与使用体验
  • Kindle Comic Converter:漫画电子书制作的专业工具
  • armapi:面向ATIM ARM模块的LPWAN嵌入式C/C++ API库
  • 量子诺亚方舟:在芯片保存人类文明
  • 抖音批量下载神器:免费一键收藏创作者全部作品