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 将渲染队列划分为五个预定义分区,每个分区拥有默认的数值基准和特定的渲染语义:

队列名称 默认值 数值范围 典型用途 深度写入 排序方式
Background 1000 0--1499 天空盒、远景、背景色 ✅ 开启 从前往后
Geometry 2000 1500--2399 不透明物体(墙体、地面、角色模型) ✅ 开启 从前往后
AlphaTest 2450 2400--2649 Alpha 测试(镂空植被、栅栏纹理) ✅ 开启 从前往后
Transparent 3000 2500--3999 半透明物体(玻璃、粒子、水、火焰) ❌ 关闭 从后往前
Overlay 4000 4000+ UI 叠加层、镜头光晕、最顶层特效 ❌ 关闭 不排序

关键要点

  • Geometry(2000) 是默认队列。大多数不透明材质无需额外设置。
  • Transparent(3000) 是半透明渲染的核心队列。此队列中的物体关闭深度写入(ZWrite Off),仅依赖深度测试。
  • AlphaTest(2450) 位于分界点附近,既需要 Alpha 裁剪又需要深度写入,是处理镂空效果的关键。
  • Unity 允许在默认值基础上偏移,例如 "Queue"="Transparent+100" 表示 3100。

半透明物体为什么渲染顺序如此重要

半透明渲染的核心在于 Alpha Blending(Alpha 混合),其公式为:

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 示例:

cs 复制代码
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" = "Transparent+100" → 渲染队列值 = 3100
"Queue" = "Transparent-50" → 渲染队列值 = 2950

偏移范围为 −249 到 +249(不超过相邻默认值的间距)。

Shader 中渲染队列相关设置一览

设置项 说明
Queue "Transparent" 设置渲染队列为 Transparent(默认 3000)
Blend SrcAlpha OneMinusSrcAlpha 标准 Alpha 混合模式
ZWrite Off 关闭深度写入,避免遮挡后续透明物体
ZTest LEqual 保留深度测试(默认值),用于被不透明物体正确遮挡
RenderType "Transparent" 渲染类型标签,用于 Shader Replacement 和材质分类

通过代码动态修改渲染队列

在某些运行时场景下,你可能需要通过 C# 脚本动态修改材质的渲染队列:

cs 复制代码
using UnityEngine;


public class RenderQueueController : MonoBehaviour

{

    public Renderer targetRenderer;

    public int customQueue = 3000;


    void Start()

    {

        // 通过 Material 直接设置渲染队列

        targetRenderer.material.renderQueue = customQueue;

    }

}

URP 还引入了独有的 Priority 属性,用于控制同一渲染队列内材质的排序优先级:

cs 复制代码
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 Layer

URP 支持基于 Sorting Layer 的分层渲染,主要用于 2D 和 UI 元素:

cs 复制代码
/ 在 URP 中,Sorting Layer 在以下范围内生效:

// - RenderQueue 在 [0, 2500] 范围内(不透明物体)

// - RenderQueue 在 [2501, 5000] 范围内(透明物体)

// 注意:跨越 2500 分界点时 Sorting Layer 不生效


var renderer = GetComponent<Renderer>();

renderer.sortingLayerName = "TransparentFX";

renderer.sortingOrder = 10;  // 值越大越后渲染(越在上面)

7.2 RenderObjects Renderer Feature

URP 的 RenderObjects Renderer Feature 允许你在渲染管线的特定位置插入自定义渲染通道:

cs 复制代码
// 在 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、特效等:

cs 复制代码
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

            .GetComponent<UniversalAdditionalCameraData>();


        // 将 Overlay Camera 添加到 Camera Stack

        cameraData.cameraStack.Add(overlayCamera);

    }

}

常见问题与解决方案

问题 1:半透明物体之间的渲染顺序不正确

症状

两个重叠的半透明物体 A 和 B,当摄像机从不同角度观察时,A 和 B 的遮挡关系不正确,甚至出现闪烁。

调整 Queue 偏移

在 Shader 中通过 "Queue"="Transparent+10" 让需要后渲染的物体获得更大的队列值。

使用 Sorting Order

对于 Sprite 或 Particle System,通过 Inspector 面板调整 Sorting LayerOrder in Layer

拆分为多个渲染通道

对于相交的半透明网格体,考虑使用 RenderObjects Feature 在特定时机分批渲染。

问题 2:半透明物体被不透明物体遮挡但显示异常

症状

半透明物体在不透明物体后面时应该完全不可见,但仍然显示了半透明的颜色。

原因 :半透明物体关闭了深度写入但保留了深度测试(ZTest LEqual),问题可能出在物体的 渲染顺序 上------如果半透明物体先于不透明物体渲染,深度缓冲区中还没有不透明物体的深度信息。

cs 复制代码
/ 不透明物体 Shader(保持默认队列即可)

Tags { "Queue" = "Geometry" }    // 2000,先渲染


// 半透明物体 Shader(队列值必须 > 2500)

Tags { "Queue" = "Transparent" } // 3000,后渲染

问题 3:Alpha Test 与 Transparent 混合使用导致渲染错误

AlphaTest(Queue 2450)和 Transparent(Queue 3000)是两种不同的半透明处理方式:

特性 Alpha Test (裁剪) Alpha Blend (混合)
效果 像素要么完全显示,要么完全丢弃 像素与背景颜色混合
边缘 锯齿感(硬边缘) 平滑过渡(软边缘)
深度写入 ✅ 开启 ❌ 关闭
性能 较好(Hi-Z 友好) 较差(需要排序和混合)
适用场景 镂空树叶、栅栏、字符轮廓 玻璃、水、粒子、烟雾

💡 最佳实践

如果材质需要镂空效果且不涉及半透明混合,使用 AlphaTest 队列 + clip() 函数。这比 Transparent 更高效,因为保留了深度写入,可以正确参与 Early-Z 优化。在 URP 中可通过 AlphaClipping 属性或 clip(discard) 指令实现。

问题 4:粒子系统与其他半透明物体排序冲突

粒子系统的每个粒子是独立的四边形面片,排序非常复杂。常见解决方案:

  • 使用 "Queue" = "Transparent+100" 让粒子在普通半透明物体之后渲染。
  • 使用加法混合 Blend One One 避免排序依赖(加法混合结果与顺序无关)。
  • 使用深度预通道(Depth Prepass)确保粒子被不透明物体正确遮挡。

总结与最佳实践

🎯 核心要点回顾

  1. 渲染队列值越小越先渲染,这是所有排序的基础。
  2. 2500 是不透明与半透明的关键分界点,务必确保半透明物体的队列值 ≥ 2500。
  3. 半透明物体必须 ZWrite Off,否则会遮挡同层其他半透明物体。
  4. 不透明物体从前往后渲染 (利用 Early-Z),半透明物体从后往前渲染(保证混合正确)。
  5. 能使用 AlphaTest 就不要用 Transparent,前者保留深度写入,性能更优。
  6. 加法混合(Blend One One)不受排序影响,适合火焰、光晕等粒子效果。

掌握 Unity URP 的渲染队列机制,是解决半透明物体渲染问题的关键。理解每个队列分区的含义、深度写入与测试的关系、以及不同排序策略的适用场景,能够帮助你在项目中有效避免透明渲染的常见陷阱,同时兼顾视觉正确性与渲染性能。

相关推荐
mxwin3 小时前
Unity Shader Alpha Test 与 Alpha Blend:透明度测试与混合的实现及排序问题
unity·游戏引擎
小贺儿开发3 小时前
Unity3D 拼图互动游戏
游戏·unity·人机交互·2d·拼图·互动
mxwin4 小时前
Unity Mask 贴图:用一张纹理的 RGBA 通道分别控制 PBR 材质参数
unity·材质·贴图
FairGuard手游加固5 小时前
FairGuard支持HybridCLR热更DLL加密
游戏·unity·游戏引擎
海海不瞌睡(捏捏王子)5 小时前
Unity GUI优化
unity·游戏引擎
心前阳光7 小时前
Unity之Luban表格配置
unity
mascon8 小时前
unity mcp 使用
unity·游戏引擎
心前阳光8 小时前
Unity之语音提问,语音答复
unity·游戏引擎
mxwin10 小时前
Unity Shader UV 坐标与纹理平铺Tiling & Offset 深度解析
unity·游戏引擎·shader·uv