【渲染流水线】[逐片元阶段]-[深度写入]以UnityURP为例

  • 深度写入阶段决定了物体深度值是否写入深度缓冲区以进行遮挡计算;操作状态ZWrite On/Off控制这一写入行为,影响渲染顺序和半透明效果的正确性。

【从UnityURP开始探索游戏渲染】专栏-直达

简介

深度缓冲区机制

  • 每个像素的深度值被存储为二维矩阵,尺寸与屏幕尺寸匹配。当渲染新物体时,系统比较当前像素深度与缓冲区值,决定是否覆盖颜色并更新深度(通过ZTest)。

写入阶段操作

  • 深度写入是光栅化的子步骤,若开启(ZWrite On),则将物体深度写入缓冲区;若关闭(ZWrite Off),则跳过写入,避免干扰后续渲染顺序。

URP特定流程

  • URP优化了传统管线,深度写入与模板测试(Stencil)并行执行,确保高性能渲染,尤其在半透明物体处理中需分离写入与颜色混合阶段。

操作状态ZWrite On/Off简介

  • ZWrite状态通过ShaderLab命令设置,直接影响渲染效果:

ZWrite On

  • ZWrite On:开启深度写入(默认值),将片元深度值写入深度缓冲‌
  • 默认状态,适用于不透明物体。开启深度写入后,物体深度值覆盖缓冲区原有值,确保近处物体正确遮挡远处物体(基于深度测试规则如LEqual)。例如,不透明模型必须开启以避免渲染顺序混乱。

ZWrite Off

  • ZWrite Off:关闭深度写入,常用于透明物体渲染‌
  • 用于半透明或特效物体。关闭写入后,深度缓冲区不会被更新,防止半透明物体遮挡后续物体,但需配合从后往前的渲染顺序(如Transparent队列)确保混合正确。典型案例包括UI元素或粒子效果,关闭可避免自身穿透问题。

状态切换场景

  • 在Shader中通过ZWrite On|Off声明,或在材质球暴露开关(如[Enum(Off,0,On,1)]_ZWrite)实现动态控制。URP中,半透明物体常采用双Pass策略:第一Pass仅写入深度(ZWrite On),第二Pass关闭写入并进行颜色混合
  • 结合RenderQueue标签控制渲染顺序,例如透明物体通常设置为"Queue"="Transparent"并关闭深度写入‌
  • 通过Camera.depthTextureMode生成深度纹理,用于屏幕空间效果(如雾效、边缘检测)

发展历史

深度写入技术在Unity中的演变反映了渲染管线的优化:

早期阶段

  • 深度缓冲概念起源于图形学基础(如Z-buffering),Unity内置管线沿用OpenGL标准,ZWrite作为核心状态由ShaderLab控制,默认开启且依赖CPU排序解决半透明问题16。

URP引入后

  • URP(2018年起)强化了性能与跨平台支持,但保留ZWrite核心机制。差异在于URP统一了渲染队列管理(如Background至Overlay队列),减少了手动排序需求;同时,深度写入阶段更紧密集成于可编程渲染管线(SRP),支持自定义Pass实现高效深度处理47。

当前进展

  • 2025年版本进一步完善,例如URP的后处理兼容性提升,深度写入与模板测试协同优化,减少了半透明渲染错误;未来趋势聚焦于实时渲染中深度写入的自动化管理

‌应用1:不透明物体的标准深度写入‌

场景

  • 确保不透明物体正确遮挡后续渲染的物体,避免穿透问题。

原理

  • 默认开启ZWrite On,将深度值写入缓冲区,配合ZTest LEqual实现遮挡。

  • StandardOpaque.shader

    • 功能‌:适用于URP管线的不透明物体渲染,强制写入深度值
    • 特点 ‌:使用Geometry队列,符合URP的UniversalPipeline标签规范
    c 复制代码
    Shader "Custom/StandardOpaque" {
        Properties { _BaseColor ("Color", Color) = (1,1,1,1) }
        SubShader {
            Tags { 
                "RenderType"="Opaque" 
                "RenderPipeline"="UniversalPipeline"
                "Queue"="Geometry"
            }
            Pass {
                ZWrite On
                HLSLPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
                struct Attributes { float4 positionOS : POSITION; };
                struct Varyings { float4 positionHCS : SV_POSITION; };
                Varyings vert(Attributes IN) {
                    Varyings OUT;
                    OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
                    return OUT;
                }
                half4 _BaseColor;
                half4 frag(Varyings IN) : SV_Target { return _BaseColor; }
                ENDHLSL
            }
        }
    }

‌应用2:半透明物体的深度预写入(Depth Prepass)‌

场景

  • 半透明物体需先写入深度但不混合颜色,解决排序问题。

原理

  • 第一Pass仅写入深度(ZWrite On),第二Pass关闭写入并混合颜色。

TransparentPrepass.shader

  • 功能‌:通过双Pass分离深度写入与颜色混合,优化半透明渲染
  • 特点 ‌:使用URP的Core.hlsl库,支持纹理采样与Alpha混合
c 复制代码
 "Custom/TransparentPrepass" {
    Properties { 
        _BaseColor ("Color", Color) = (1,0,0,0.5) 
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader {
        Tags { 
            "RenderType"="Transparent" 
            "RenderPipeline"="UniversalPipeline"
            "Queue"="Transparent"
        }
        // Pass 1: 仅写入深度
        Pass {
            ZWrite On
            ColorMask 0
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            struct Attributes { float4 positionOS : POSITION; };
            struct Varyings { float4 positionHCS : SV_POSITION; };
            Varyings vert(Attributes IN) {
                Varyings OUT;
                OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
                return OUT;
            }
            half4 frag(Varyings IN) : SV_Target { return 0; }
            ENDHLSL
        }
        // Pass 2: 混合颜色
        Pass {
            ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            TEXTURE2D(_MainTex); SAMPLER(sampler_MainTex);
            struct Attributes { 
                float4 positionOS : POSITION; 
                float2 uv : TEXCOORD0; 
            };
            struct Varyings { 
                float4 positionHCS : SV_POSITION; 
                float2 uv : TEXCOORD0; 
            };
            Varyings vert(Attributes IN) {
                Varyings OUT;
                OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
                OUT.uv = IN.uv;
                return OUT;
            }
            half4 _BaseColor;
            half4 frag(Varyings IN) : SV_Target {
                half4 col = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, IN.uv) * _BaseColor;
                return col;
            }
            ENDHLSL
        }
    }
}

‌应用3:水体效果的深度交互‌

场景

  • 水体需读取场景深度并关闭自身深度写入,实现半透明与深度交互。

原理

  • 关闭ZWrite,通过_CameraDepthTexture计算水体与场景的深度差。

WaterEffect.shader

  • 功能‌:动态计算水体与场景深度差,实现边缘渐变效果
  • 特点 ‌:使用URP的DeclareDepthTexture.hlsl访问深度图,需在URP设置中启用深度纹理
c 复制代码
Shader "Custom/WaterEffect" {
    Properties {
        _WaterColor ("Color", Color) = (0,0.5,1,0.8)
        _DepthFade ("Depth Fade", Range(0,1)) = 0.5
    }
    SubShader {
        Tags { 
            "RenderType"="Transparent" 
            "RenderPipeline"="UniversalPipeline"
            "Queue"="Transparent+100"
        }
        Pass {
            ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"
            struct Attributes { float4 positionOS : POSITION; };
            struct Varyings { 
                float4 positionHCS : SV_POSITION;
                float4 screenPos : TEXCOORD0;
            };
            Varyings vert(Attributes IN) {
                Varyings OUT;
                OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
                OUT.screenPos = ComputeScreenPos(OUT.positionHCS);
                return OUT;
            }
            half4 _WaterColor;
            half _DepthFade;
            half4 frag(Varyings IN) : SV_Target {
                float2 uv = IN.screenPos.xy / IN.screenPos.w;
                float sceneDepth = LinearEyeDepth(SampleSceneDepth(uv), _ZBufferParams);
                float waterDepth = IN.screenPos.w;
                float depthDiff = saturate((sceneDepth - waterDepth) * _DepthFade);
                return half4(_WaterColor.rgb, _WaterColor.a * depthDiff);
            }
            ENDHLSL
        }
    }
}

【从UnityURP开始探索游戏渲染】专栏-直达

(欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,🙏)

相关推荐
霍格沃兹测试学院-小舟畅学15 分钟前
Cypress:架构原理与环境设置全解析
架构
Aaron15881 小时前
基于RFSOC+VU13P+GPU架构在雷达电子战的技术
人工智能·算法·fpga开发·架构·硬件工程·信号处理·基带工程
林义满1 小时前
大促零宕机背后的运维升级:长三角中小跨境电商的架构优化实践
大数据·运维·架构
前端不太难1 小时前
如何给 RN 项目设计「不会失控」的导航分层模型
前端·javascript·架构
田里的水稻2 小时前
运维_SOC芯片的架构综述
运维·架构
三更两点2 小时前
构建企业级智能体AI系统的七层架构
人工智能·架构
IManiy5 小时前
总结之高并发场景下的缓存架构技术方案分析
缓存·架构
卡奥斯开源社区官方5 小时前
鸿蒙智行 L3 内测启幕:从技术架构到商用落地的全链路技术拆
华为·架构·harmonyos
一水鉴天5 小时前
整体设计 定稿 之31 拼语言统筹表 - “归” 档位属 多轴联动(codebuddy)
人工智能·架构
HuangYongbiao8 小时前
NestJS 架构设计系列:应用服务与领域服务的区别
后端·架构