当前位置: 首页 > article >正文

图形 3.5 Early-z和Z-prepass

Early-z和Z-prepass

B站视频:图形 3.5 Early-z和Z-prepass

文章目录

  • Early-z和Z-prepass
  • 深度测试(Depth Test)
    • 传统渲染管线中的深度测试
    • 逐片元测试
    • 解决物体可见遮挡性的问题
    • 流程图
    • 代码逻辑
    • 带来的问题
  • 提前深度测试(Early-Z)
    • 传统渲染管线中的提前深度测试
    • Early-Z失效
    • 高效利用Early-Z
  • Z-Prepass
    • 方法一:双Pass法
      • 动态批处理的问题
    • 方法二:提前分离的PrePass
      • 使用URP下的RendererFeature
    • 透明渲染的解决方案
    • Z-Prepass的计算消耗
  • Early-Z **和** Z-Prepass的实例应用
    • 面片叠加的头发渲染
      • 最初方案
      • 性能改善
      • 优化的方案

深度测试(Depth Test)

传统渲染管线中的深度测试

3.5_1

逐片元测试

3.5_2

解决物体可见遮挡性的问题

例如需渲染一紫一橙两个三角形(如下图):

3.5_3

  • 紫色三角形的深度值为5,与深度缓冲区中的默认值 ∞ \infty 进行比对,5小于 ∞ \infty ,故写入深度缓冲区中;
  • 黄色三角形在渲染时,与深度缓冲区中的值进行比对,小于缓冲区的值则进行替换,最终结果如右图。

流程图

3.5_4

代码逻辑

//对每一个三角形
foreach(triangle T){
    //对每一个三角形中的片元
	foreach(fragment(x,y,z) in T){	
        //深度测试:如果片元深度小于ZBuffer深度
		if(fragment.z < ZBuffer[x,y]){
            //更新颜色
			FrameBuffer[x,y] = fragment.rgb;	
            //更新深度 
			ZBuffer[x,y] = fragment.z;		       
		} 
        //深度测试失败 
		else{					       	
			//什么都不做,片元数据被丢弃        
		}    
	}
}

带来的问题

未通过测试的片元将会被丢弃,导致片元的计算就白白浪费了,造成一些性能的问题。

3.5_5

提前深度测试(Early-Z)

传统渲染管线中的提前深度测试

提前深度测试(Early-Z)发生在光栅化之后,片元着色器之前。

3.5_6

还是上面的例子:

3.5_7

注:提前深度测试阶段仍然可以搭配模板测试(stencil test)使用

Early-Z失效

在以下几种情况下,Early-Z会失效:

  1. 开启Alpha Test 或 clip/discard 等手动丢弃片元操作;

    还是以上图为例子,若物体1在shader中被手动discard了,物体1不会被渲染,但是其深度还在,Early-Z阶段会认为物体1仍然挡住了后续的物体,导致后续的物体2不能被正确的渲染出来。

  2. 手动修改GPU插值得到的深度;

  3. 开启Alpha Blend;

    进行透明度混合的物体,一般不会开启深度写入(ZWrite Off)。

  4. 关闭深度测试Depth Test。

一旦进行了以上操作,那么GPU就会关闭early-z直到下次clear z-buffer后才会重新开启(不过现在的gpu也在逐渐优化,使其更智能开关early-z)。之所以gpu会选择关闭early-z是因为上述那些操作可能会在片元阶段与late-z阶段之间修改深度缓存中的深度值,导致提前的early-z的结果并不正确。我们也可以在fragment shader中使用layout(early_fragment_tests)来强制打开early-z。

高效利用Early-Z

如果将不透明物体从远至近进行渲染,Early-Z将不会带来任何优化效果。

3.5_8

此外,如果通过CPU将不透明物体从近至远排序,在场景十分复杂的情况下,将会带来额外的性能损耗,且可能导致合批操作无法正常进行。

Z-Prepass

方法一:双Pass法

  • 在第一个Pass即Z-Prepass中仅仅只写入深度,不计算输出任何颜色;
  • 在第二个Pass中关闭深度写入,并且将深度比较函数设置为相等(Equal)。
Shader "Custom/EarlyZPresentation"{   
    Properties{ 
        //.......
    }   
    SubShader{        
    	Tags { "RenderType"="Opaque" }        
    	//Z-Prepass部分        
    	Pass        
    	{        	
            //开启深度写入
        	ZWrite On	     
            //关闭颜色输出         
        	ColorMask 0	   
        	CGPROGRAM            
            	//...这里省略了单纯地顶点变换计算部分            
       	 	ENDCG        
    	}        
    	//正常地计算输出颜色的Pass        
    	Pass        
    	{        	
            //关闭深度写入       
        	Zwrite Off	     
            //深度相等为通过
        	ZTest Equal	            
        	CGPROGRAM           
            	//...这里省略了顶点变换和颜色等计算部分            
        	ENDCG        
    	}    
    }
}

动态批处理的问题

一个物体的材质球shader若是多Pass,则无法参与合批,带来Drawcall问题。

3.5_9

方法二:提前分离的PrePass

仍然使用两个Pass,但:

  • 将原先第一个Pass(即Z-Prepass)单独分离出来为单独一个Shader,并先使用这个Shader将整个场景的Opaque的物体渲染一遍;
  • 而原先材质只剩下原先的第二个Pass,仍然关闭深度写入,并且将深度比较函数设置为相等。

使用URP下的RendererFeature

3.5_10

3.5_11

透明渲染的解决方案

Z-Prepass也是透明渲染的一种解决方案,可防止复杂的半透明物体内部深度穿插的问题。

3.5_12

Z-Prepass的计算消耗

引用国外论坛的帖子。

3.5_13

条件性能
没有Z-PrePass + 简单的几何变换光栅Pass0ms + 几何变换光栅Pass ~2.7ms = ~2.7ms
有Z-PrePass + 简单的几何变换光栅PassZ-PrePass ~2.0ms + 几何变换光栅Pass ~2.4ms = ~4.4ms

因此是否使用Z-Prepass因根据项目实际情况去使用,例如在一个场景中有非常多的OverDraw,且不能很好的将不透明物体从前往后进行排序,那么使用Z-PrePass的计算消耗要远小于这些OverDraw。

Early-Z Z-Prepass的实例应用

面片叠加的头发渲染

如下图,该头发效果有多个半透明面片构成,需要从后往前进行排序渲染才能得到正确的透明度混合结果。

3.5_14

最初方案

有三个Pass:

  • 第一个Pass处理不透明的部分。
    • 开启透明度测试,仅通过不透明的像素;
    • 关闭背面剔除;
    • 开启深度写入,并将深度测试通过条件为小于(Less);
  • 第二个Pass处理半透明的背面部分。
    • 开启透明度测试,仅通过半透明的像素;
    • 剔除正面部分;
    • 关闭深度写入,并将深度测试通过条件为小于(Less);
  • 第二个Pass处理半透明的正面部分。
    • 开启透明度测试,仅通过半透明的像素;
    • 剔除背面部分;
    • 关闭深度写入,并将深度测试通过条件为小于(Less);

3.5_16

性能改善

  • 广泛使用Early-Z,以避免计算消耗巨大的片元着色器;
  • 通常一半的头发都藏在头的后面;
    • 先将头部渲染出来
  • 使用透明度测试时,无法使用Early-Z;
    • 解决方案:使用一个简单的shader进行透明度测试,形成一个Z-Buffer(即为Z-PrePass)
    • 在后续测试中使用深度测试,而不是透明度测试,以实现相同的效果
  • Early-Z节省相当多的开销。

在这里插入图片描述

优化的方案

有四个Pass:

  • 第一个Pass写入深度。
    • 开启透明度测试,仅通过不透明的像素;
    • 关闭背面剔除;
    • 开启深度写入,并将深度测试通过条件为小于(Less);
    • 不写入任何颜色;
    • 使用简单的片元着色器,仅返回透明度;
  • 第二个Pass处理不透明的部分。
    • 关闭背面剔除;
    • 关闭深度写入,并将深度测试通过条件为等于(Equal);
  • 第三个Pass处理半透明的背面部分。
    • 剔除正面部分;
    • 关闭深度写入,并将深度测试通过条件为小于(Less);
  • 第四个Pass处理半透明的正面部分。
    • 剔除背面部分;
    • 关闭深度写入,并将深度测试通过条件为小于(Less);

3.5_18


http://www.kler.cn/a/466355.html

相关文章:

  • 音视频入门基础:MPEG2-PS专题(3)——MPEG2-PS格式简介
  • 力扣hot100——栈
  • Docker新手:在tencent云上实现Python服务打包到容器
  • Springboot 下载附件
  • Pycharm连接远程解释器
  • 使用npm 插件[mmdc]将.mmd时序图转换为图片
  • Mysql监视器搭建
  • FPGA、STM32、ESP32、RP2040等5大板卡,结合AI,更突出模拟+数字+控制+算法
  • 仓储机器人底盘的研究
  • 在Microsoft Windows上安装MySQL
  • 2025年第五届控制理论与应用国际会议 | Ei Scopus双检索
  • 「Mac畅玩鸿蒙与硬件53」UI互动应用篇30 - 打卡提醒小应用
  • Chapter2 文本规范化
  • #C02L02P01. C02.L02.一维数组最值问题.知识点1.求最大值
  • Elasticsearch:利用 AutoOps 检测长时间运行的搜索查询
  • 【2025最新计算机毕业设计】基于SpringBoot+Vue智慧养老医护系统(高质量源码,提供文档,免费部署到本地)【提供源码+答辩PPT+文档+项目部署】
  • unity学习2:关于最近github的2FA(two-factor authentication)新认证
  • 深入理解正则表达式及基本使用教程
  • 图像转换 VM与其他格式互转
  • CLIP论文笔记
  • 2025年度全国会计专业技术资格考试 (甘肃考区)报名公告
  • 从 SQL 到 SPL:分组后每组前面增加符合条件的记录
  • 分布式练手:Server
  • 如何得到深度学习模型的参数量和计算复杂度
  • 【图像处理】OpenCv + Python 实现 Photoshop 中的色彩平衡功能
  • 机器学习经典算法——逻辑回归