深入URP之Shader篇4: Depth Only Pass

news2024/11/13 11:32:24

Depth only pass

unlit shader中包含了一个Depth Only Pass,这个pass的代码在Packages\com.unity.render-pipelines.universal\Shaders\DepthOnlyPass.hlsl中。这是一个公共pass,几乎所有的URP shader都会包含这个pass。本篇说一说这个pass的作用以及实现细节。

作用

Depth only pass的作用是生成一张场景的深度图,一般是在渲染不透明物体之前,对所有包含该pass的材质对应的物体执行这个pass,当所有物体执行完毕后,就得到了深度图。这个pass执行的前提是URP判断需要深度图,比如URP assets中配置了:

在这里插入图片描述

或者camera开启了后处理。使用深度图,游戏可以从屏幕空间深度重建出世界空间深度,从而完成很多有意思的效果,比如水面渲染的岸边泡沫,粒子面片进入地面时柔和的过渡。另外很多屏幕空间的后处理效果都会用到深度图。

但是如果只是为了得到深度图,其实也不必执行depth only pass,只要在不透明物体渲染之后,从当前的depth buffer copy出深度就可以。所以这个pass也不是必然执行的。主要看copy depth的条件是否满足,如果满足则执行copy depth,而不是depth only pass。具体的逻辑,可以参考ForwardRenderer.cs代码,注意是URP的c#代码,不是shader:

            // Depth prepass is generated in the following cases:
            // - If game or offscreen camera requires it we check if we can copy the depth from the rendering opaques pass and use that instead.
            // - Scene or preview cameras always require a depth texture. We do a depth pre-pass to simplify it and it shouldn't matter much for editor.
            // - Render passes require it
            bool requiresDepthPrepass = requiresDepthTexture && !CanCopyDepth(ref renderingData.cameraData);
            requiresDepthPrepass |= isSceneViewCamera;
            requiresDepthPrepass |= isPreviewCamera;
            requiresDepthPrepass |= renderPassInputs.requiresDepthPrepass;
            requiresDepthPrepass |= renderPassInputs.requiresNormalsTexture;

重点是CanCopyDepth方法:

        bool CanCopyDepth(ref CameraData cameraData)
        {
            bool msaaEnabledForCamera = cameraData.cameraTargetDescriptor.msaaSamples > 1;
            bool supportsTextureCopy = SystemInfo.copyTextureSupport != CopyTextureSupport.None;
            bool supportsDepthTarget = RenderingUtils.SupportsRenderTextureFormat(RenderTextureFormat.Depth);
            bool supportsDepthCopy = !msaaEnabledForCamera && (supportsDepthTarget || supportsTextureCopy);

            // TODO:  We don't have support to highp Texture2DMS currently and this breaks depth precision.
            // currently disabling it until shader changes kick in.
            //bool msaaDepthResolve = msaaEnabledForCamera && SystemInfo.supportsMultisampledTextures != 0;
            bool msaaDepthResolve = false;
            return supportsDepthCopy || msaaDepthResolve;
        }

从该函数可以看到,如果Camera开启了MSAA,则就不能使用Copy Depth了,为啥呢?注释有说,URP暂时不能在不损失精度的前提下从MSAA RT resolve出depth。在PC上,这几乎是唯一的限制,如果把MSAA关闭,在FrameDebugger中就会发现Depth Only Pass消失了,而在DrawOpaqueObjects之后多了一个CopyDepth的pass。

Z-Pre Pass和Early-Z测试

一般来说,这种在渲染场景之前,先把场景的深度渲染出来的过程叫做Z-Pre Pass(或者Depth Pre Pass等),其用途除了生成深度图之外,最重要的作用是给硬件Early-Z的执行提供一个优化好的Depth Buffer。Early-Z在Fragment Shader之前执行,如果片段不能通过Early-Z测试,则不会执行Fragment Shader,这可以大大降低Overdraw。但Early-Z可以起作用的条件在于我们绘制的顺序是从近到远(对于不透明物体),而正常渲染时并不能保证这个顺序。通过Z-Pre Pass,渲染一遍场景之后,depth buffer中保存的是离camera最近的这些片段的z值,之后再正常渲染场景,此时进行Early-Z测试就会发现只有和depth buffer中z值相等的片段才需要绘制,即只要进行一个Equal测试就可以排除掉所有潜在的overdraw片段,让Early-Z的作用发挥到最大。另外Early-Z本身也是有条件的,对于一个draw call,如果FS中执行了clip操作,或者修改了深度值,那么就不能进行Early-Z测试,GPU就会使用正常的Late-Z测试。如果我们要绘制大量使用Alpha Test材质的物体,比如一大片草地,这些草本身overdraw就很严重,还不能启用Early-Z,对性能影响很大。但如果使用Z-Pre Pass,就可以在此时对于草的draw call使用alpha test来更新depth buffer,这样那些透明片段就会被丢弃掉,留下的片段的深度值会被写入到depth buffer上(当前前提上通过深度测试),经过Z-Pre Pass之后,再普通pass中绘制草时就可以不使用Alpha Test了,这样Early-Z可以使用了,仍然是简单的进行深度的Equal测试就可以将所有应该被画出来的片段画出来。

看到这儿,相信你心中已经有了想法,既然很多效果需要深度图,而做一个Z-Pre Pass又可以优化Ealry-Z,一举两得嘛,还需要CopyDepth干嘛。想法是很好,可惜的是Unity的Depth only pass并不能作为一个优化Early-Z的Z-Pre Pass,因为Depth only pass的输出是一个depth Render texture,而不是depth buffer,这样后面普通的pass绘制时,执行深度测试没法用这个RT去比较。深度测试使用的当前Render Target的depth attachment,且必须是同一个Render Target。后面的普通Pass的Render Target是_cameraColorTexture,它有自己的depth attachment。总之,可惜了,白白执行了那么多draw call,只得到了深度图,而不能优化Early-Z。

这个事情的原因可参考Unity论坛的官方解释:https://forum.unity.com/threads/need-clarification-on-urps-use-of-the-depth-prepass.1004577/
总之,未来是可以解决的,暂时还不行。

Shader代码分析

由于只是输出depth texture,其VS代码也很简单,就是计算clip space坐标即可。但是URP的这个代码稍微复杂一些:

Varyings DepthOnlyVertex(Attributes input)
{
    Varyings output = (Varyings)0;
    UNITY_SETUP_INSTANCE_ID(input);
    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);

    output.uv = TRANSFORM_TEX(input.texcoord, _BaseMap);
    output.positionCS = TransformObjectToHClip(input.position.xyz);
    return output;
}

half4 DepthOnlyFragment(Varyings input) : SV_TARGET
{
    UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);

    Alpha(SampleAlbedoAlpha(input.uv, TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap)).a, _BaseColor, _Cutoff);
    return 0;
}

VS中会计算uv坐标,为啥呢?在FS中可以看到,会采样贴图中的alpha。这儿有两个函数:

half Alpha(half albedoAlpha, half4 color, half cutoff)
{
#if !defined(_SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A) && !defined(_GLOSSINESS_FROM_BASE_ALPHA)
    half alpha = albedoAlpha * color.a;
#else
    half alpha = color.a;
#endif

#if defined(_ALPHATEST_ON)
    clip(alpha - cutoff);
#endif

    return alpha;
}

half4 SampleAlbedoAlpha(float2 uv, TEXTURE2D_PARAM(albedoAlphaMap, sampler_albedoAlphaMap))
{
    return SAMPLE_TEXTURE2D(albedoAlphaMap, sampler_albedoAlphaMap, uv);
}

即,URP的depth only pass会执行Alpha Test,且Alpha值除了来源于颜色本身,也来源于贴图的Alpha,当然需要开启相应的关键字。URP的做法当然是对的,因为对于Alpha Test材质的物体,其深度必然也受Alpha Test影响。其实渲染阴影贴图也一样,对于Alpha Test材质都需要处理。

本篇小结

URP的这个Depth Only Pass Shader本身比较简单,没啥可说,但是这个Depth Only Pass确实要说道说道,必须要了解到当前版本下面这个Depth Only Pass不能起到Z-Pre Pass的作用去优化Early-Z,因此如果可能直接使用Copy Depth可以节省大量的draw call,当然前提是不能使用MSAA,在低端移动设备上是一个可以考虑的优化选项,关闭MSAA,然后使用一个基于后处理的AA代替,即节省了MSAA的开销,又节省了Depth Only Pass的draw call,一石二鸟!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/60501.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Ubuntu映射到Windows网络驱动器

将虚拟机Ubuntu映射到Windows网络驱动器中,我们需要Ubuntu的网络和主机网络处于同一网段下,然后使Ubuntu具备共享文件功能,最后在windows下添加网络地址。 将Ubuntu设置和主机同一网段 查看主机网络信息 在虚拟机中 选择编辑-- 虚拟网络编…

Java的字符串String

文章目录什么是字符串String类的声明为什么我们的String是不可变的为什么String类用final修饰String的创建字符串比较相等关于Java中的比较关于字符串不同赋值操作对应的内存分配那对象如何进行比较内容字符串常量池StringTalbe的位置字符串常见的操作拼接操作获得字符串的子串…

事件驱动的微服务、CQRS、SAGA、Axon、Spring Boot

事件驱动的微服务、CQRS、SAGA、Axon、Spring Boot 学习构建分布式事件驱动的微服务、CQRS、事件溯源、SAGA、事务 课程英文名:Event-Driven Microservices, CQRS, SAGA, Axon, Spring Boot 此视频教程共10.0小时,中英双语字幕,画质清晰无…

一个带有楼中楼的评论系统数据库设置思路

前言 有个需求,需要实现百度贴吧那样能评论帖子中某一楼的评论里的评论 分析 说起来有点拗口,其实这个评论系统分为4个部分: 主题(楼主发布的帖子)直接返回楼主的评论(从帖):直接…

(11)点云数据处理学习——Colored point cloud registration(彩色点注册)

1、主要参考 (1)官网介绍 Colored point cloud registration — Open3D 0.16.0 documentation 2、原理和实现 2.1原理 本教程演示了使用几何形状和颜色进行配准的ICP变体。实现了[Park2017]算法。颜色信息锁定沿切平面的对齐。因此,该算法…

Yocto创建自己的分区(基于STM32MP1)

Yocto创建自己的分区(基于STM32MP1) 前几章节我们分析了machine class里面几篇关键的class,还有machine conf里面的inc文件,大致的创建分区的流程都比较清晰了,本章节动手实际操作一把,创建一个自己的分区…

Unity中的协程

一、什么是协程 协程(Coroutines) 是一种比线程更加轻量级的存在,也被称为用户态线程一个进程可以拥有多个线程,一个线程可以拥有多个协程协程并不会增加线程,它在线程中运行,通过分时复用的方式运行多个协程,其切换代…

《Spring 5.x源码解析之Spring AOP 注解驱动使用及其实现原理》

《Spring 5.x源码解析之Spring AOP 注解驱动使用及其实现原理》 学好路更宽,钱多少加班。---- mercyblitz 一、前言 大家好,欢迎阅读《Spring 5.x源码解析》系列,本篇作为该系列的第二篇,重点介绍Spring AOP在注解驱动编程模式上的…

基于J2EE的大型视频影音系统的设计与实现

目 录 毕业设计(论文)任务书 I 摘 要 II ABSTRACT III 第1章 绪 论 1 1.1 课题的提出 1 1.1.1 Web2.0浪潮进一步影响全球互联网发展 1 1.1.2 视频分享成为2.0浪潮的最新爆发点 1 1.2 系统研究目的 2 1.3 系统设计目标 2 第2章 关键技术介绍 4 2.1 网页…

C#使用策略模式或者委托替代多IfElse判断和Switch语句

这篇文件介绍使用设计模式中的策略模式和委托来解决多个IfElse判断语句和Switch语句,这种替换方式在其他语言也一样可以做到,比如PHP、JavaScript、Python或者Java等。 这里以C#为例进行演示。 需要为一个程序编写计算方法,根据标签名称来决定…

【华为上机真题 2022】TLV解码

🎈 作者:Linux猿 🎈 简介:CSDN博客专家🏆,华为云享专家🏆,Linux、C/C、云计算、物联网、面试、刷题、算法尽管咨询我,关注我,有问题私聊! &…

abc280

D 解法1&#xff0c;直接暴力&#xff0c;答案一定在2~1e6里面或者k本身&#xff08;如果k是个质数的话&#xff09; #include<bits/stdc.h> using namespace std; signed main() {long long k;cin>>k;for(long long i1;i<2000010;i) {k/__gcd(k,i);if(k1) {co…

在Linux中,使用Docker,安装es和kibana

1.部署单点es 1.1.创建网络 因为我们还需要部署kibana容器&#xff0c;因此需要让es和kibana容器互联。这里先创建一个网络&#xff1a; # 创建一个网络&#xff1a;es-net docker network create es-net# 查看本机的网络 docker network ls# 删除一个网络&#xff1a;es-ne…

Allegro如何缩放数据操作指导

Allegro如何缩放数据操作指导 Allegeo上可以缩放数据,尤其是在做结构时候非常有用,具体操作如下 以下图为例,需要把这个数据缩小0.5倍 点击Create Detail命令 Option里面选定一个层面,比如放在Board Geomertry,silkscreen top层 Scaling Factor输入0.5 Find选择所有 …

UE5 中 LiveLink 的开发全流程教程

注意&#xff0c;需要有源代码版本的 Unreal Engine&#xff0c;而不是从游戏 Launcher 中下载的 Unreal 版本。 本文使用是 Unreal Engine 5.1 版本。关于一些基础 API 介绍&#xff0c;可以参考之前的一篇。 起点 可以将 Engine\Source\Programs\BlankProgram 作为模板拷贝…

虚拟机搭载Linux · VMware + Ubuntu 部署 路线参考(20.04.5)

提前回家&#xff0c;要部署OS的实验环境。感谢广源同学给予的帮助和支持~ 电脑文件系统进行了整理&#xff0c;重型文件大部分转移到移动硬盘上。 &#xff08;解压了好久然后我找到镜像源了呜呜没发过来&#xff09; 一、VMware 16 安装 VMware虚拟机安装Linux教程(超详细)…

详解 Spring Boot 项目中的日志文件

目录 1. 日志的作用 2. 自定义日志打印 2.1 日志的基本格式 2.2 得到日志对象 2.3 使用日志对象提供的方法&#xff0c; 打印自定义的日志内容 2.4 日志框架的说明 3. 日志的持久化 3.1 配置日志文件的文件名 3.2 配置日志文件的保存路径 3.3 持久化日志的特性 4. 日…

Java集合(Collection List Set Map)

文章目录Collection接口和常用方法Collection接口遍历元素方式1 -使用Iterator(迭代器)Collection接口遍历对象方式2-for循环增强List接口和常用方法List[ArrayList, LinkedList, Vector]的三种遍历方式ArrayList的注意事项ArrayList的底层操作机制源码分析Vector和ArrayList的…

【已解决】vue后台页面跳转无法正常显示

今天写后端&#xff0c;发现一个问题&#xff0c;我的其他页面之间都可以正常跳转显示&#xff0c;但是我的其中一个页面&#xff08;简称U页面&#xff09;&#xff0c;我跳转到U页面时还可以显示&#xff0c;但之后点击其他页面就无法正常显示了&#xff08;能跳转不能显示&a…

chapter6——流水线的艺术

目录1.影响最大时钟频率的因素2.流水线3.DLX指令集的实现4.流水线对吞吐率的影响5.流水线原理6.流水线冒险结构冒险数据冒险控制冒险其他冒险对高速ASIC日益增长的需求使得越来越需要增加电路每个时钟周期的计算吞吐率。可以通过流水线提高ASIC在这方面的性能&#xff0c;但是也…