目录
- 一、 模版测试
- 1.1 模版测试的原理
- 1.2 模版测试核心配置
- 1.3 应用
- 二、深度测试
- 2.1 深度测试原理
- 2.2 基础概念
- 2.2.1 Early-Z
- 2.2.2 ZTest比较操作
- 2.2.3 深度缓冲区
- 2.2.4 ZWrite深度写入
- 2.2.5 渲染队列
- 2.3 深度值特性
- 2.4 深度测试核心配置
- 2.5 深度测试应用
- 2.6 总结
- 参考资料
一、 模版测试
1.1 模版测试的原理
模版缓冲区每个像素存储1个8位无符号整数,范围0-255 默认值0
- 原理:当前片元参考值与模版缓冲区指定值比较,通过保留片元,否则丢弃;1输出原本颜色,0不输出
从渲染管线理解:(图中流程均在frag之后)
frag阶段高度可配置,但无法编程
stencil test位置:alpha测试之后,深度测试之前
其他阶段说明:
Pixel OwnerShip Test像素权测试:在scene、game视图里的可视像素
Scissor Test裁剪测试:自定义渲染区域
Alpha Clip:按照阈值,剔除半透明区域;即alpha低于如0.7,都被剔除
- 常用场景:传送门(里面是另一个房间场景)(也可以用另一个相机拍RT)
参考游戏:《笼中窥梦》
1.2 模版测试核心配置
| 配置项 | 功能说明 |
|---|---|
| Ref(参考值) | 自定义0-255的数值,用于和模板缓冲区值做比较 |
| Read Mask(读掩码) | 与参考值、模板缓冲区值做按位与运算后再进行比较 |
| Write Mask(写掩码) | 控制模板缓冲区可写入的位范围 |
| Comp(比较操作) | 可选Never、Less、LEqual、Equal、NotEqual、Greater、GEqual、Always共8种规则 |
| Pass | 模板测试通过时对模板缓冲区执行的操作 |
| Fail | 模板测试失败时对模板缓冲区执行的操作 |
| ZFail | 模板测试通过但深度测试失败时对模板缓冲区执行的操作 |
| Color Mask | 颜色遮罩,设为0时不输出任何颜色,仅用于更新模板缓冲区 |
1.3 应用
思路:外界、边界、内界
即渲染物体->渲染mask->渲染被mask遮罩的物体
- 3d卡牌
效果:卡牌呈现纵深3d感,随视角变化,卡牌内也有视差变化
原来的完整场景,将卡牌边框外的东西都剔除 - 多面盒子
- 描边
- 反射
- 阴影体
- 蒙版
二、深度测试
2.1 深度测试原理
- 原理:
- 通过深度缓冲区,判断片元的前后遮挡关系
- 针对当前对象在屏幕上(更准确的说是frame buffer)对应的像素点,将像素自身深度值与当前像素点缓冲的像素点比较;若通过,本对象的像素点才将颜色写入颜色缓冲,否则不写入。
- 位置:模版测试之后、alpha混合之前
深度缓冲区:存储每个像素深度值,精度为24位,高端显卡支持32位
深度测试的发展:
早期算法:画家算法(从远往近,层层覆盖,会产生大量overdraw,与现在相反被淘汰)、Z-Buffer算法:通过深度缓冲区存储像素深度值,自动比较前后关系,当前主流方案
->
控制Z-Buffer对深度值的存储:ZTest、ZWrite
->
控制不同类型物体的渲染顺序(渲染队列):透明、不透明物体
->
减少overdraw:Early-Z、Z-cull、Z-check
2.2 基础概念
2.2.1 Early-Z
- 定义:frag前进行深度测试,直接丢弃未通过测试的面片,降低无效片元在frag的计算
- 位置:光栅化后、frag之前
注意:有时候会用到两次深度测试,Early-Z一次、FS处理后ZTest再进行一次起到检查作用;前次Z-Cull,后次Z-Check;
2.2.2 ZTest比较操作
| ZTest 状态 | 描述 |
|---|---|
| Greater | 深度大于当前缓存则通过 |
| LEqual | 深度小于等于当前缓存则通过 |
| Less | 深度小于当前缓存则通过 |
| GEqual | 深度大于等于当前缓存则通过 |
| Equal | 深度等于当前缓存则通过 |
| NotEqual | 深度不等于当前缓存则通过 |
| Always(Off) | 深度不论如何都通过 |
| Never | 深度不论如何都不通过 |
2.2.3 深度缓冲区
- 定义:类似颜色缓冲(储存所有的像素颜色),每个像素存储信息。跟颜色缓冲一样有宽度和高度。
深度缓冲是由窗口系统自动创信息,它会以16、24或32位float的形式诸存它的深度值。
在大部分的系统中,深度缓冲的精度都是24位 - z-buffer里存储当前深度信息
2.2.4 ZWrite深度写入
深度写入包括两种状态:ZWrite On与ZWrite Off
先ZTest、再ZWrite
ZTest:读深度缓冲,拿当前片元 Z 和缓冲里已存 Z
ZWrite:写深度缓冲,把本片元的 Z 存进去,只有测试成功才会执行写操作
ZTest分为通过和不通过两种情况,ZWrite分为开启和关闭两种情况的话,一共就是四种情况:
| 序号 | 状态组合 | 结果 |
|---|---|---|
| 1 | 深度测试通过,深度写入开启 | 写入深度缓冲区,写入颜色缓冲区 |
| 2 | 深度测试通过,深度写入关闭 | 不写深度缓冲区,写入颜色缓冲区 |
| 3 | 深度测试失败 | 不写深度缓冲区,不写颜色缓冲区 |
渲染呈现:
ZWrite On:渲染队列>Always>LEqual(越靠前越先渲染)
ZWrite Off:不写入深度,被后来者覆盖;除非渲染队列更大
←——————相机前方—————— 相机位置 —————相机后方————→ -10 -5 0 +5 +10 方块B 方块A 相机-1,1->0,1 方块a更近,覆盖b
ps:shader中默认状态是ZTest LEqual、ZWrite On
2.2.5 渲染队列
- 内置渲染队列介绍
队列数越小,越先渲染;队列数越大,越后渲染。
| 队列名称 | 队列号 | 描述 |
|---|---|---|
| Background | 1000 | 最早被渲染的物体的队列 |
| Geometry | 2000 | 不透明物体的渲染队列。大多数物体都应该使用该队列进行渲染,也是Unity Shader中默认的渲染队列 |
| AlphaTest | 2450 | 有透明通道,需要进行Alpha Test的物体的队列,比在Geometry中更有效 |
| Transparent | 3000 | 半透物体的渲染队列。一般是不写深度的物体,Alpha Blend等的在该队列渲染 |
| Overlay | 4000 | 最后被渲染的物体的队列,一般是覆盖效果,比如镜头光晕,屏幕贴片之类的 |
设置位置
- Shader代码中设置:
Tags { "Queue" = "Transparent" },默认是Geometry - Shader / Material 的 Inspector面板,可以查看Render Queue相关属性;在此处设置,会覆盖shader里的设置
- Shader代码中设置:
物体渲染顺序规则
- 不透明物体的渲染顺序:从前往后
- 透明物体的渲染顺序:从后往前(OverDraw)
2.3 深度值特性
- 深度缓冲区:
- 存储0-1之间非线性缓冲值,因为在适应透视相机的投影阶段(viewport),视椎体->前后裁剪面一致大小的面;此时深度值映射变为非线性,近处深度值间间隔宽,越远越密集(想象人眼看通向远处的树)
- 利用有限的缓冲区精度,近平面分配高精度、远平面分配低精度(近清晰、远模糊)
ps:时刻结合生活中真实的视觉效果理解
- 空间变化:
模型空间(无深度)->世界空间(线性,z)->视空间(线性,获取场景内线性深度就是转到这一步,z)->裁剪空间(线性,M(Perspective)z,A·z + B)->NDC(非线性,z/w)->屏幕空间
- 深度冲突(Z-Fighting):两个平面极近平行时,深度缓冲区精度不足导致导致前后像素频繁切换,造成画面频闪
解决:- . 物理上拉开物体之间距离
- 调远摄像机近平面,提高近处深度精度
- 使用更高精度的32位缓冲区
2.4 深度测试核心配置
- 核心配置两项:
(1)Unity内置渲染队列(队列号越小越先渲染)
| 队列名称 | 队列号 | 适用场景 | 渲染顺序规则 |
|---|---|---|---|
| Background | 1000 | 背景 | - |
| Geometry | 2000 | 不透明物体 | 从前往后渲染,减少Overdraw |
| AlphaTest | 2450 | 透明度测试物体 | - |
| Transparent | 3000 | 半透明物体 | 从后往前渲染,保证混合效果正确 |
| Overlay | 4000 | 镜头光晕、屏幕贴片等覆盖效果 | - |
(2)ZTest与ZWrite组合
| 序号 | 状态组合 | 结果 |
|---|---|---|
| 1 | 深度测试通过,深度写入开启 | 写入深度缓冲区,写入颜色缓冲区 |
| 2 | 深度测试通过,深度写入关闭 | 不写深度缓冲区,写入颜色缓冲区 |
| 3 | 深度测试失败 | 不写深度缓冲区,不写颜色缓冲区 |
- 举例:被遮挡物体的显示
Greater,ABC, 深度:A>B>C
如果B要展示在A之上,
Pass 0 :正常绘制 ZTest:Lequal ZWrite:On
Pass 1 :被遮挡发光 ZTest:Greater ZWrite:Off
被遮挡部分通过测试,绘制在A上;如果背景深度是无穷远,不通过,露出部分不绘制
不写入深度:他后面的物体还是Lequal,都大于这个深度,无法正确绘制在屏幕上
适用场景:被建筑物等遮挡时,人物依然有轮廓显示
- 注意
(1)Queue是SubShader 标签,Pass没有独立生效的渲染队列。
材质的渲染队列默认使用inspector里设置的,会覆盖shader的队列设置
(2)透明混合的pass出现在不透明阶段
原因:材质队列2000,某pass使用Blend SrcAlpha OneMinusSrcAlpha混合
(3)URP下,所有Pass无法保证按顺序书写
UPR根据
- 渲染阶段
- LightMode
- 管线绘制
安排pass,不会默认把shader内所有pass都执行;
会执行的pass:
① 标准用途:LightMode为以下,都会执行:
| LightMode | 用途 |
|---|---|
UniversalForward | 前向渲染路径中绘制物体 |
UniversalForwardOnly | 始终以前向方式绘制,兼容前向和延迟 Renderer |
UniversalGBuffer | 延迟渲染时写入 GBuffer |
SRPDefaultUnlit | 未设置LightMode时的默认值,也用于额外绘制,如描边 |
ShadowCaster | 写入阴影贴图,让物体投射阴影 |
DepthOnly | 深度预通过,输出相机视角的深度 |
DepthNormalsOnly | 深度与法线预通过,配合 SSAO 等功能 |
Universal2D | 供 2D Renderer 使用 |
Meta | 光照贴图烘焙时提取材质信息 |
② 相同LightMode默认只执行第一个匹配项
③ 其他pass要自己设置renderer feature
④没写LightMode的 Pass 会被视为SRPDefaultUnlit,不是任意数量的无标签 Pass 都会自动执行
2.5 深度测试应用
- 基于深度着色:水体渲染:比较跟水底的深度,做颜色渐变
- 阴影贴图:比较灯光空间和视空间的深度差
- 透明物体、粒子渲染(透掉黑底)
- 角色遮挡透视效果
- 能量罩切边效果
2.6 总结
- 深度缓冲重要的两个值:当前深度缓冲值(ZBufferValue)与深度参考值(rederenceValue),通过比较抉择是否更换深度缓冲值
- Early-Z能节省开销,提前抛弃会造成overdraw的无效片元
- 深度测试通过ZTest、ZWrite完成控制,如半透明、透明的渲染
- 深度缓冲区存储的是0-1范围内的浮点,非线性
- 深度渲染顺序:先渲染不透明物体,从前往后;再渲染透明物体,从后往前
如有错漏,欢迎指正!
参考资料
1.【【技术美术百人计划】图形 3.1 深度与模板测试 传送门效果示例】 https://www.bilibili.com/video/BV1Tb4y1C7Qa/?share_source=copy_web&vd_source=ffa9f839ea8afb4bd7809dfc7ae336f8