Unity Shader 渲染队列 (Render Queue):控制 Geometry、Transparent、Overlay 等队列确保半透明物体渲染正确

张开发
2026/4/7 17:54:13 15 分钟阅读

分享文章

Unity Shader 渲染队列 (Render Queue):控制 Geometry、Transparent、Overlay 等队列确保半透明物体渲染正确
深入理解 Unity URP 渲染队列的排序机制、关键分界点与实际应用策略彻底解决半透明物体渲染顺序问题。什么是渲染队列 (Render Queue)在实时图形渲染中渲染队列Render Queue是控制物体绘制先后顺序的核心机制。Unity 中每个 Shader 都可以通过Queue标签指定一个整数值这个数值决定了该 Shader 所在的物体在渲染管线中的绘制时机。核心排序规则非常简单 核心规则数值越小越先渲染数值越大越后渲染。后渲染的像素会覆盖先渲染的像素。这一机制是保证半透明物体正确混合的前提。Unity 预定义了五个关键的队列分区每个分区对应一个默认数值范围和特定的渲染语义。理解这些分区的含义和分界点是掌握渲染顺序的基础。URP 渲染管线完整流程在深入各个队列之前我们先看 URP Forward 渲染路径的完整阶段划分。理解整体流程有助于定位渲染队列在其中扮演的角色。从图中可以看出渲染队列在Opaque不透明物体和Transparent透明物体阶段之间扮演着关键的分流角色。数值2500是这两大阶段的分界点也是半透明渲染问题的核心所在。五大关键队列分区详解Unity 将渲染队列划分为五个预定义分区每个分区拥有默认的数值基准和特定的渲染语义队列名称默认值数值范围典型用途深度写入排序方式Background10000–1499天空盒、远景、背景色✅ 开启从前往后Geometry20001500–2399不透明物体墙体、地面、角色模型✅ 开启从前往后AlphaTest24502400–2649Alpha 测试镂空植被、栅栏纹理✅ 开启从前往后Transparent30002500–3999半透明物体玻璃、粒子、水、火焰❌ 关闭从后往前Overlay40004000UI 叠加层、镜头光晕、最顶层特效❌ 关闭不排序关键要点Geometry2000是默认队列。大多数不透明材质无需额外设置。Transparent3000是半透明渲染的核心队列。此队列中的物体关闭深度写入ZWrite Off仅依赖深度测试。AlphaTest2450位于分界点附近既需要 Alpha 裁剪又需要深度写入是处理镂空效果的关键。Unity 允许在默认值基础上偏移例如QueueTransparent100表示 3100。半透明物体为什么渲染顺序如此重要半透明渲染的核心在于Alpha BlendingAlpha 混合其公式为Alpha 混合公式Formula// 最终颜色 源颜色 × 源Alpha 目标颜色 × (1 - 源Alpha) C_final C_src × α_src C_dst × (1 - α_src)这个公式揭示了一个关键问题混合结果依赖于目标颜色即已经绘制在帧缓冲区中的颜色。这意味着半透明物体必须在其背景物体渲染完毕之后才能进行正确的混合计算。⚠️ 深度写入冲突半透明物体如果开启了深度写入ZWrite On会阻止其后面更远的半透明物体被渲染——因为深度测试会失败。因此 Transparent 队列默认关闭深度写入但这同时引入了半透明物体之间的排序问题。为什么不透明物体可以从前往后渲染不透明物体完全不透明Alpha 1不存在混合需求。从前往后渲染可以充分利用Early-Z优化近处的物体先写入深度缓冲远处的物体在逐像素测试时因深度失败而被直接丢弃从而避免无效的光照计算减少 Overdraw。在 Shader 中设置渲染队列渲染队列通过 ShaderLab 的Tags块设置。以下是一个完整的半透明 Shader 示例Shader Custom/TransparentObject { Properties { _BaseColor (Base Color, Color) (1,1,1,0.5) _MainTex (Main Texture, 2D) white {} } SubShader { // ★ 关键设置渲染队列为 Transparent Tags { RenderType Transparent Queue Transparent // 默认值 3000 RenderPipeline UniversalPipeline } // ★ 关键设置混合模式 Blend SrcAlpha OneMinusSrcAlpha // ★ 关键关闭深度写入半透明物体必须 ZWrite Off // 深度测试仍然开启利用不透明物体的深度信息 ZTest LEqual Pass { Name Forward Tags { LightMode UniversalForward } HLSLPROGRAM #pragma vertex vert #pragma fragment frag #include Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl struct Attributes { float4 positionOS : POSITION; float2 uv : TEXCOORD0; }; struct Varyings { float4 positionCS : SV_POSITION; float2 uv : TEXCOORD0; }; sampler2D _MainTex; float4 _MainTex_ST; float4 _BaseColor; Varyings vert(Attributes input) { Varyings output; output.positionCS TransformObjectToHClip(input.positionOS.xyz); output.uv TRANSFORM_TEX(input.uv, _MainTex); return output; } float4 frag(Varyings input) : SV_Target { float4 texColor tex2D(_MainTex, input.uv); return texColor * _BaseColor; } ENDHLSL } } } Queue 偏移语法可以在默认队列值上添加偏移量来控制同类型材质之间的渲染顺序。Queue Transparent100→ 渲染队列值 3100Queue Transparent-50→ 渲染队列值 2950偏移范围为 −249 到 249不超过相邻默认值的间距。Shader 中渲染队列相关设置一览设置项值说明QueueTransparent设置渲染队列为 Transparent默认 3000BlendSrcAlpha OneMinusSrcAlpha标准 Alpha 混合模式ZWriteOff关闭深度写入避免遮挡后续透明物体ZTestLEqual保留深度测试默认值用于被不透明物体正确遮挡RenderTypeTransparent渲染类型标签用于 Shader Replacement 和材质分类通过代码动态修改渲染队列在某些运行时场景下你可能需要通过 C# 脚本动态修改材质的渲染队列using UnityEngine; public class RenderQueueController : MonoBehaviour { public Renderer targetRenderer; public int customQueue 3000; void Start() { // 通过 Material 直接设置渲染队列 targetRenderer.material.renderQueue customQueue; } }URP 还引入了独有的Priority属性用于控制同一渲染队列内材质的排序优先级using UnityEngine; using UnityEngine.Rendering; public class URPRenderPriority : MonoBehaviour { public Material transparentMat; void Start() { // Priority 值越小渲染越晚越在上面 // 适合让特定透明物体显示在其他透明物体之上 transparentMat.renderQueue (int)RenderQueue.Transparent; transparentMat.SetPropertyBlock(null); // URP Renderer 中的 priority 属性 // material.renderQueue 控制队列分组 // Renderer.priority 控制同组内的微调排序 } }⚠️ Material 实例化警告直接修改renderer.material.renderQueue会创建材质实例导致额外的内存开销。如果只是修改渲染队列建议使用Renderer.sharedMaterial.renderQueue谨慎使用会影响所有使用该材质的物体或MaterialPropertyBlock。URP 中的渲染顺序控制进阶除了基础的渲染队列设置URP 还提供了多种高级机制来精细控制渲染顺序7.1 Sorting Layer 与 Order in LayerURP 支持基于 Sorting Layer 的分层渲染主要用于 2D 和 UI 元素/ 在 URP 中Sorting Layer 在以下范围内生效 // - RenderQueue 在 [0, 2500] 范围内不透明物体 // - RenderQueue 在 [2501, 5000] 范围内透明物体 // 注意跨越 2500 分界点时 Sorting Layer 不生效 var renderer GetComponentRenderer(); renderer.sortingLayerName TransparentFX; renderer.sortingOrder 10; // 值越大越后渲染越在上面7.2 RenderObjects Renderer FeatureURP 的RenderObjectsRenderer Feature 允许你在渲染管线的特定位置插入自定义渲染通道// 在 URP Renderer Asset 中添加 RenderObjects Feature // 1. Event: 选择插入时机 // - BeforeRenderingOpaques (不透明物体之前) // - AfterRenderingOpaques (不透明物体之后) // - AfterRenderingSkybox (天空盒之后) // - BeforeRenderingTransparents (透明物体之前) // - AfterRenderingTransparents (透明物体之后) // // 2. Filters: 选择渲染的对象 // - Layer Mask: 指定渲染哪些 Layer // - Rendering Layer Mask: 指定 Rendering Layer // // 3. Settings: 覆盖渲染状态 // - Depth: 深度测试/写入设置 // - Stencil: 模板测试设置7.3 多摄像机 Base-Overlay 模式URP 支持通过Camera Stack实现多摄像机分层渲染。Base Camera 先渲染场景Overlay Camera 在其上叠加渲染 UI、特效等using UnityEngine; using UnityEngine.Rendering.Universal; public class CameraStackSetup : MonoBehaviour { public Camera baseCamera; public Camera overlayCamera; void Start() { // 获取 Base Camera 的 Universal Additional Camera Data var cameraData baseCamera .GetComponentUniversalAdditionalCameraData(); // 将 Overlay Camera 添加到 Camera Stack cameraData.cameraStack.Add(overlayCamera); } }常见问题与解决方案问题 1半透明物体之间的渲染顺序不正确症状两个重叠的半透明物体 A 和 B当摄像机从不同角度观察时A 和 B 的遮挡关系不正确甚至出现闪烁。调整 Queue 偏移在 Shader 中通过QueueTransparent10让需要后渲染的物体获得更大的队列值。使用 Sorting Order对于 Sprite 或 Particle System通过 Inspector 面板调整Sorting Layer和Order in Layer。拆分为多个渲染通道对于相交的半透明网格体考虑使用RenderObjectsFeature 在特定时机分批渲染。问题 2半透明物体被不透明物体遮挡但显示异常症状半透明物体在不透明物体后面时应该完全不可见但仍然显示了半透明的颜色。原因半透明物体关闭了深度写入但保留了深度测试ZTest LEqual问题可能出在物体的渲染顺序上——如果半透明物体先于不透明物体渲染深度缓冲区中还没有不透明物体的深度信息。/ 不透明物体 Shader保持默认队列即可 Tags { Queue Geometry } // 2000先渲染 // 半透明物体 Shader队列值必须 2500 Tags { Queue Transparent } // 3000后渲染问题 3Alpha Test 与 Transparent 混合使用导致渲染错误AlphaTestQueue 2450和TransparentQueue 3000是两种不同的半透明处理方式特性Alpha Test (裁剪)Alpha Blend (混合)效果像素要么完全显示要么完全丢弃像素与背景颜色混合边缘锯齿感硬边缘平滑过渡软边缘深度写入✅ 开启❌ 关闭性能较好Hi-Z 友好较差需要排序和混合适用场景镂空树叶、栅栏、字符轮廓玻璃、水、粒子、烟雾 最佳实践如果材质需要镂空效果且不涉及半透明混合使用AlphaTest队列 clip()函数。这比Transparent更高效因为保留了深度写入可以正确参与 Early-Z 优化。在 URP 中可通过AlphaClipping属性或clip(discard)指令实现。问题 4粒子系统与其他半透明物体排序冲突粒子系统的每个粒子是独立的四边形面片排序非常复杂。常见解决方案使用Queue Transparent100让粒子在普通半透明物体之后渲染。使用加法混合Blend One One避免排序依赖加法混合结果与顺序无关。使用深度预通道Depth Prepass确保粒子被不透明物体正确遮挡。总结与最佳实践 核心要点回顾渲染队列值越小越先渲染这是所有排序的基础。2500 是不透明与半透明的关键分界点务必确保半透明物体的队列值 ≥ 2500。半透明物体必须 ZWrite Off否则会遮挡同层其他半透明物体。不透明物体从前往后渲染利用 Early-Z半透明物体从后往前渲染保证混合正确。能使用 AlphaTest 就不要用 Transparent前者保留深度写入性能更优。加法混合Blend One One不受排序影响适合火焰、光晕等粒子效果。掌握 Unity URP 的渲染队列机制是解决半透明物体渲染问题的关键。理解每个队列分区的含义、深度写入与测试的关系、以及不同排序策略的适用场景能够帮助你在项目中有效避免透明渲染的常见陷阱同时兼顾视觉正确性与渲染性能。

更多文章