mypro334 发表于 2022-11-30 07:33

入门精要-第十三章-深度图原理理解

写了卡渲的人物和树,已经用了两次深度图了,未来什么雪地脚印、水体都会用到,深感深度图的重要性,特开文章从头分析。
分析一下unity的深度图套餐。套餐真香,拿来就能吃,也没分析过组成成分。现在想自己做着吃喽!
原理


非线性分布、范围为的高精度深度值被存在深度纹理中,通过NDC的z分量得到。
非线性


在MVP变换的最后一步,当我们使用透视投影矩阵时,这个变换就是非线性的。mvp变换完成后,还要进行齐次除法变换为NDC,即变到[-1,1]的立方体空间。





z分量范围为[-1,1],因此要做一个映射。d=0.5·zndc+0.5
深度纹理来源


使用延迟渲染路径,深度纹理可以访问到,因为延迟渲染会把这些信息渲染到G-buffer 中。
而当无法直接获取深度缓存时,深度和法线纹理是通过一个单独的Pass 渲染而得的。具体实现是, Unity 会使用着色器替换(Shader Replacement) 技术选择那些渲染类型(SubShader RenderType)为Opaque 的物体,将渲染队列小于等于2500 (AlphaTest以下)的物体渲染到深度和法线纹理中。即半透明不行。
URP中实现


开头定义
TEXTURE2D_X_FLOAT(_CameraDepthTexture); SAMPLER(sampler_CameraDepthTexture);
为啥要用_X_FLOAT,我猜是为Instancing和更高的精度考虑。(妹学呢,instancing,久仰大名,可惜快到期末,忙成狗)。
输出结构体中增加
float4 posScr: TEXCOORD7;
顶点着色器
v.posScr = ComputeScreenPos(v.posCS);
函数源码
float4 ComputeScreenPos(float4 positionCS){    float4 o = positionCS * 0.5f;    o.xy = float2(o.x, o.y * _ProjectionParams.x) + o.w;    o.zw = positionCS.zw;    return o;}
ComputeScreenPos输入裁剪空间顶点坐标,_ProjectionParams.x在默认情况下是1(若使用翻转的投影矩阵则为-1),上述代码实际输出:
Outputx = clipx / 2 + clipw / 2;
Outputy = clipy / 2 + clipw / 2;
Outputz = clipz;
Outputw = clipw;
可以看出,这里的xy并不是真正的视口空间下的坐标,因此,要在片元着色器中除以它的w分量。

为什么不在ComputeScreenPos中除以w分量得到真正的屏幕空间坐标,因为如果Unity在顶点着色器中这么做的华,就会破坏从顶点到片元着色器的插值的结果。投影空间不是线性的,而插值是线性的。

经过除法操作后,我们可以得到该片元在视口空间的坐标了,也就是一个xy范围都在之间的值。如果是透视投影 z[-Near,Far] w, 如果是正交投影 z[-1,1],w恒为1。

片元着色器
half2 screenUV = v.posScr.xy/v.posScr.w;half depth = SAMPLE_TEXTURE2D_X(_CameraDepthTexture, sampler_CameraDepthTexture, screenUV).r;half linearEyeDepth = LinearEyeDepth(depth, _ZBufferParams);
转换到线性空间原理不多看。先会用。
这样就得到了线性空间(视角空间)下的深度值啦。
引用


Shader学习笔记(三)学习Shader所需的数学基础
页: [1]
查看完整版本: 入门精要-第十三章-深度图原理理解