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(或者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/
总之,未来是可以解决的,暂时还不行。
由于只是输出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,一石二鸟!
写在之前Shader变体、Shader属性定义技巧、自定义材质面板,这三个知识点任何一个单拿出来都是一套知识体系,不能一概而论,本文章目的在于将学习和实际工作中遇见的问题进行总结,类似于网络笔记之用,方便后续回顾查看,如有以偏概全、不祥不尽之处,还望海涵。1、Shader变体先看一段代码......Properties{ [KeywordEnum(on,off)]USL_USE_COL("IsUseColorMixTex?",int)=0 [Toggle(IS_RED_ON)]_IsRed("IsRed?",int)=0}......//中间省略,后续会有完整代码 #pragmamulti_c
Unity内置渲染管线、SRP、URP、HDRP的关系:Unity渲染管线包含内置渲染管线和SRP,内置渲染管线是Unity默认的渲染管线,不可修改;而SRP是可以用户自己控制渲染流程;URP和HDRP则相当于Unity提供的SRP模板。内置渲染管线(Build-InRender):内置渲染管线是Unity默认的渲染管线,兼容Unity面向的所有平台,但渲染次序是固定的,效果不突出。SRP(ScriptableRenderPipline):可编程渲染管线,核心是一堆API集合,使得整个渲染过程及相关配置暴露给用户,使得用户可以精确地控制项目的渲染流程。用户可以直接利用Unity的URP、HDR
解开谜团:深入探索ChatGPT的技术奇迹。ChatGpt无处不在,无论是在播客、博客、YouTube还是社交媒体上。当我注意到这项新技术如此受欢迎时,我决定试一试,我被震惊了!有很多关于ChatGpt及其魔力的博客,但在这篇博客中,我将深入探讨其内部技术及其工作原理!ChatGpt简介根据OpenAI,ChatGpt被描述为:“我们训练了一个名为ChatGpt的模型,它以对话方式进行交互。对话格式使ChatGpt可以回答后续问题、承认错误、挑战不正确的前提并拒绝不适当的请求。ChatGPT是InstructGPT的兄弟模型,它经过训练可以按照提示中的说明进行操作并提供详细的响应。”OpenA
近期,以“生成式人工智能”(GenerativeAI)为核心技术的聊天机器人ChatGPT火爆全球。百度、阿里巴巴、科大讯飞、360等国内企业纷纷抛出ChatGPT相关进展,打造中国版的ChatGPT。科大讯飞此前在投资者互动平台表示,ChatGPT主要涉及到自然语言处理相关技术,属于认知智能领域的应用之一,公司在该方向技术和应用具备长期深厚的积累。并称2022年12月已进一步启动生成式预训练大模型任务攻关,类ChatGPT技术将在今年5月率先落地科大讯飞AI学习机产品。近日,科大讯飞副总裁、研究院执行院长刘聪围绕什么是ChatGPT,它强在哪里?会对未来世界带来哪些颠覆性影响?进一步阐述Ch
导语 | 在C++11标准之前,C++中默认的传值类型均为Copy语义,即:不论是指针类型还是值类型,都将会在进行函数调用时被完整的复制一份!对于非指针而言,开销及其巨大!因此在C++11以后,引入了右值和Move语义,极大地提高了效率。本文介绍了在此场景下两个常用的标准库函数:move和forward。一、特性背景(一)Copy语义简述C++中默认为Copy语义,因此存在大量开销。以下面的代码为例:0_copy_semantics.cc#include#includeclassObject{public:Object(){std::coutv;v.push_back(obj);}最终的输出
目录引言:一、inode和block1、inode和block概述2、inode的内容1.inode包含文件的元信息(文件属性)2.用stat命令可以查看某个文件的inode信息3.Linux系统文件三个主要的时间属性 4.目录文件的结构3、inode的号码5、硬盘分区后的结构6、inode的大小7、inode的特殊作用 二、链接文件三、案例:恢复EXT类型的文件四、案例:恢复XFS类型的文件五、日志文件1.日志的功能2.日志文件的分类3.日志保存位置1.常见的一些日志文件:2.扩展:日志检查3.小结:4.日志消息的级别5.用户日志分析六、总结引言:inode是一个重要概念,是理解Uni
注意:我只是一个编码新手,所以这个问题的核心可能存在明显的错误或误解。本质上,我需要在JavaScript中“按值”深度复制多维数组到未知深度。我原以为这需要一些复杂的递归,但似乎在JavaScript中您只需要深复制一个级别就可以按值复制整个数组。举个例子,这是我的测试代码,使用了一个故意复杂的数组。functiontest(){vararr=[['ok1'],[],[[],[],[[],[[['ok2'],[]]]]]];varcloned=cloneArray(arr);arr='';//Deletetheoriginalalert(cloned);}functioncloneA
pycocotools库的主要作用:下载coco数据集,并使得操作数据集的数据更加方便。MMCV是一个面向计算机视觉的基础库,它支持了很多开源项目。好的习惯:学会在官方文档中解决的问题。目录一、安装VisualStudio2022(其他版本也可以)二、下载pycocotools三、解析Why?四、安装mmpycocotools库(mmcv有用到)五、安装mmcv-full库(1)介绍mmcv(2)安装mmcv一、安装VisualStudio2022(其他版本也可以)直接去官方下载:VisualStudio2022IDE-适用于软件开发人员的编程工具(microsoft.com)注意:网上有很多
背景说明我问了一个关于使用循环定义日期数组的问题。数组是根据名为“dateinterval”的已声明变量定义的。我设计代码的方式导致了与另一个循环相关的错误消息,另一个用户为我提供了另一个循环来解决这个问题。既然我已经仔细比较了两种不同的解决方案,我就是不明白为什么它们不会产生相同的结果。我的代码我开发了以下代码来定义UTC格式的日期数组。然而,结果是自1970年1月1日00:00:00以来以毫秒为单位的日期数组。换句话说,一个数字。for(vari=0;i正确的解决方案下面的代码是另一位用户提供给我的正确代码(再次感谢您!)此代码定义了一组UTC日期。for(vari=0;i我不明白
下图是生命周期的说明图:如图可以看到:当创建编解码器的时候处于未初始化状态。首先你需要调用configure(…)方法让它处于Configured状态,然后调用start()方法让其处于Executing状态。在Executing状态下,你就可以使用上面提到的缓冲区来处理数据。Executing的状态下也分为三种子状态:Flushed,Running、End-of-Stream。在start()调用后,编解码器处于Flushed状态,这个状态下它保存着所有的缓冲区。一旦第一个输入buffer出现了,编解码器就会自动运行到Running的状态。当带有end-of-stream标志的buffer进