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

相关推荐
LF男男12 小时前
TouchManager
unity·c#
mxwin12 小时前
Unity Shader 径向模糊与径向 UV 变形速度感 · 冲击波效果完全指南
unity·游戏引擎·shader·uv
weixin_4239950013 小时前
unity 微信开发小游戏,网络资源获取数据
unity·游戏引擎
Yasin Chen13 小时前
Unity TMP_SDF 分析(五)片元着色器
unity·游戏引擎·着色器
mxwin14 小时前
Unity Shader Texture Bombing用随机旋转与偏移的多次采样,打破大地形纹理的
unity·游戏引擎
代数狂人14 小时前
《深入浅出Godot 4与C# 3D游戏开发》第二章:编辑器导航
3d·编辑器·游戏引擎·godot
zcc85807976215 小时前
Unity MVVM UniTask + 轻量级 ReactiveProperty
unity
zcc85807976216 小时前
Unity 自动生成UI绑定+MVVM 架构模板
unity
LF男男17 小时前
MK - Grand Mahjong Game-
unity·c#
呆呆敲代码的小Y18 小时前
【Unity实战篇】| YooAsset + UOS CDN 云服务资源部署,实现正式热更流程
人工智能·游戏·unity·游戏引擎·免费游戏