前言
在之前写卡渲时因对深度图的了解不够,导致对等宽边缘光困惑了不少时间,为了加深对unity 中深度的理解,查了不少资源,发现不少讲的都有问题,但还好自己琢磨解决了。本文对其进行了总结。总的来说介绍了unity中深度是如何计算的,两种深度模式及获取对应模式的深度图,为什么得到的深度图全黑?
测试环境
- Unity 2022.3.12f1
- win10
- JetBrains Rider 2023.2
Unity如何计算深度?
- 在之前学习dx12时,笔者了解到的深度缓冲区中的深度是从近到远,值从0~1,最近为0,最远为1。但在unity中,不同平台下的深度表示都不同,如Android平台下,从近到远,深度值由0到1;windows(DX11) / IOS下,从近到远,深度值由1到0.本文以windows(DX11)为例,来看看unity 中深度是如何计算的
-
在图形学中,物体的显示需要经过MVP变换至齐次裁剪空间,这一过程就不推导了,总的来说齐次裁剪矩阵如下:
那么任一点V(只考虑z值的话)乘以矩阵P 可得 Z = (\frac{zf - nf}{f - n}),分别将n和f代入该式子。可得:z = 0;z = f,因此可以得出在齐次裁剪空间中z值范围为[0, f]
-
但在unity中得到的深度图的深度是基于NDC空间,因此还需要对上述求得的z除以w(因为刚刚求得的w = z,而0 <= n <= z <= f)进行规范化,可得Z = \frac{zf - nf}{z(f - n)},依然分别将n和f代入该式子,可得:z = 0; z = 1,这里求得在NDC空间中z值范围为[0,1]。等等!这和笔者之前说的完全是相反的,在windows下近屏幕的z = 1, 远屏幕的z = 0,这是为什么呢?
-
原来unity为我们做了一次反转操作:z_{new} = 1 - z。为什么需要反转呢?
- 先来看看不反转带来的问题
如下图所示,可以看出当近远平面越接近,深度精度越高;当物体逐渐远离近平面,深度精度越低(两个z值接近的图元难以判断其远近,这也就是z-fighting的原因)
-
反转后
下图是反转后的曲线。看起来好像然并卵?事情并没有这么简单,上述存在的问题是,当图元越远离近平面,depth精度越低,但浮点数有一个优点:当浮点数越接近0时,精度越高。因此在下图中,越远离近平面,z值越小越接近0,反而精度更高
那么最终计算公式为depth =\frac{n(f - z)}{z(f - n)} ,当图元处于近平面,depth = 1;当图元处于远平面,depth = 0
- 先来看看不反转带来的问题
三种深度图模式
- URP中,深度图有三种模式:CopyDepth Pass、DepthPrepass、DepthNormalsPrepass
CopyDepth Pass
-
该Pass会将Render Queue为Geometry&AlphaTest的Opaque图元写入深度图_CameraDepthTexture,也就是说该深度图在渲染完不透明物体后才有
-
启用CopyDepth Pass
- 启用Render data Asset的"Depth texture"
-
关闭Render data的"SSAO"
- 启用Render data Asset的"Depth texture"
DepthPrepass
- 该Pass在阴影渲染后执行,并写入深度图_CameraDepthTexture
- 启用DepthPrepass
- 启用Render data Asset的"Depth texture"
- 启用Render data Asset的"Depth texture"
- 打开Render data的"SSAO",并将"Source"设为"Depth"
- 在shader中添加Pass"DepthOnly":DepthPrepass会将pass为"DepthOnly"的图元写入深度图
DepthNormalsPrepass
- DepthNormalsPrepass代替DepthPrepass,同样在阴影渲染后执行,但它不仅写入深度_CameraDepthTexture,还写入法线_CameraNormalsTexture(法线范围[-1, 1])
- 启用
- 启用Render data Asset的"Depth texture"
- 启用Render data Asset的"Depth texture"
- 打开Render data的"SSAO",并将"Source"设为"Depth"
- 在shader中添加Pass"DepthNormals":DepthNormalsPrepass会将pass为"DepthNormals"的图元写入深度图
三种深度
Raw Depth
- Raw Depth表示深度缓冲区的原始数据,近平面为1,远平面为0
Linear 01 Depth
- Linear 01 Depth表示将Raw Depth变换为线性,且近平面为0,远平面为1
Linear Eye Depth
- Linear Eye Depth表示沿相机z轴的深度,且深度范围不再是[0,1],而是unity中的距离单位(如1表示沿z轴距离相机1个距离单位)
为什么得到的深度图全黑
-
如果您仔细看了以上内容,并且设置无误,那么问题很有可能在camera(反正笔者是这样的)。需要设定相机的近远平面
-
其余可能全黑的原因:渲染顺序,及是否添加对应Pass
reference
https://zhuanlan.zhihu.com/p/389971233
https://zhuanlan.zhihu.com/p/648793922
Comments | NOTHING