【渲染流水线】[逐片元阶段]-[深度写入]以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开始探索游戏渲染】专栏-直达

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

相关推荐
天空属于哈夫克33 天前
企业微信二次开发:精准实现关键词自动回复
架构·企业微信
晨米酱3 天前
AGENTS.md:Agent 的上下文策略层
面试·架构·agent
这个DBA有点耶3 天前
MVCC深入:Read View、版本链与快照读——InnoDB并发控制的内核
数据库·mysql·架构
码流子3 天前
高速公路安全监测实践:碰撞监测预警+物联网底座,从感知到处置的闭环
大数据·人工智能·物联网·算法·架构
moMo3 天前
从固定流程到问题路由:让 LangGraph RAG 按需检索
架构
白远山3 天前
上海24小时自助健身房解决方案实战指南与经验分享
java·数据库·架构·需求分析
LorryJovens3 天前
【LAAP科研】双系统具身AGI范式研究——基于LAAP认知架构与Jev概率决策模型的系统性技术调研与范式验证
人工智能·gpt·安全·架构
JQweryuiop3 天前
中小型游戏陪练工作室数字化管理实践:基于七彩屋电竞护航小程序订单、履约与绩效系统的落地复盘
大数据·游戏·小程序·架构
LONGZETECH3 天前
一线职教实测:风光 580 汽车故障诊断仿真系统,破解实车实训四大核心痛点
人工智能·学习·安全·架构·汽车
萧瑟余晖3 天前
Netty 核心组件与 Reactor 模型详解
网络·架构