前言

在之前写卡渲时因对深度图的了解不够,导致对等宽边缘光困惑了不少时间,为了加深对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

    1. 启用Render data Asset的"Depth texture"

    2. 关闭Render data的"SSAO"

DepthPrepass

  • 该Pass在阴影渲染后执行,并写入深度图_CameraDepthTexture
  • 启用DepthPrepass
    1. 启用Render data Asset的"Depth texture"
    2. 打开Render data的"SSAO",并将"Source"设为"Depth"
    3. 在shader中添加Pass"DepthOnly":DepthPrepass会将pass为"DepthOnly"的图元写入深度图

DepthNormalsPrepass

  • DepthNormalsPrepass代替DepthPrepass,同样在阴影渲染后执行,但它不仅写入深度_CameraDepthTexture,还写入法线_CameraNormalsTexture(法线范围[-1, 1])
  • 启用
    1. 启用Render data Asset的"Depth texture"
    2. 打开Render data的"SSAO",并将"Source"设为"Depth"
    3. 在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


他们曾如此骄傲的活过,贯彻始终