此篇内容是基于深度图的应用的说明,对深度图不清楚的,请查看:
深度图的说明
- 关于相机的深度侧视图:
深度:存的是Z值,不是此点到相机的距离(例如 G点的深度是AH,但是AG是G点到相机的距离)
(1)第二个函数 LinearEyeDepth
此函数返回就是相机空间中的Z值
(2)第一个函数:Linear01Depth
此函数的返回值是(0,1)的线性值,也是相机空间(视角空间)中的
0:相机的位置是0
1:远裁剪平面是1
在相机空间中的Z值是深度最原始的值,将Z值/原裁剪平面far,就转换到了(0,1)的范围内
此处附上《Shader入门精要》中的推到过程:
采样深度图推导过程请点击此处查看。
下面通过深度图重建世界坐标系:
(方法一)逆矩阵方式重建
:在frag着色器中得到uv坐标(已经进行过齐次除法,且已经由【-1,1】换算到【0,1】),采样深度图得到Clip裁剪空间中的Z值
所以就得到了Clip裁剪空间中的 坐标(x,y,z).然后乘以VP矩阵的逆矩阵,就转换到世界空间中的坐标(记得/.w)
下面是逆推的演示:
条件:已知条件(M为VP矩阵,M^-1即为其逆矩阵,Clip为裁剪空间,ndc为标准设备空间,world为世界空间):
(1)
ndc = Clip.xyzw / Clip.w = Clip / Clip.w
world = M^-1 * Clip
二者结合得:
world = M ^-1 * ndc * Clip.w
(2)
我们已知M和ndc,然而还是不知道Clip.w,但是有一个特殊情况,是world的w坐标,经过变换后应该是1,即
1 = world.w = (M^-1 * ndc).w * Clip.w
进而得到Clip.w = 1 / (M^ -1 * ndc).w
带入上面等式得到:
world = (M ^ -1 * ndc) / (M ^ -1 * ndc).w
所以,世界坐标就等于ndc进行VP逆变换之后再除以自身的w。
(3)缺点
不过这种方式重建世界坐标,性能比较差,一般来说,我们都是逐顶点地进行矩阵运算,毕竟定点数一般还是比较少的,但是全屏幕逐像素进行矩阵运算,这个计算量就不是一般的大了,性能肯定是吃不消的。
(方法二):屏幕射线插值方式重建视空间坐标
假如我们得到了 相机在世界空间中的位置A,并且已知一个方向向量AG
那么:G点在世界空间中的位置 = 世界空间相机位置A 沿着AG方向 平移AG的长度
代码和实现方式如下:
(3) 计算ScreenUV ,的方式:
- 在vertex中 使用 ComputeScreenPos 函数计算:
此函数执行齐次除法, 归一化, 这两个步骤混合起来就是 下面的计算
//
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;
}
- 在vertex中计算的 pos : SV_POSITION
(1) 此pos 在vertex中是齐次裁剪坐标
(2)此pos 在fragment中是 屏幕坐标 (屏幕坐标的范围:(0,0)到 (width,hight)),在fragment中计算,视口坐标 直接用 pos / 屏幕宽高
(3)用上面的结果,采样 _CameraDepthTexture得到缓冲区存储的深度
struct Fragment
{
float2 positionSS;
// 屏幕坐标
float2 screenUV;
// 此点自身在视图空间的深度
float depth;
// 在深度图中存储的深度(可能不是此点本身的深度)
float bufferDepth;
};
///
// positionSS 是传入的 positionSS :SV_POSITION的值
// 在Vertex 函数中 positionSS 是齐次裁剪坐标,此时并未进行齐次除法 也 没有归一化
// 在 Fragment函数中 positionSS 是屏幕坐标 (此时 距离转换到 视口坐标 screenUV 坐标, 就差一个 屏幕宽高的缩放了)
Fragment GetFragment(float4 positionSS)
{
Fragment f;
f.positionSS = positionSS.xy;
f.screenUV = f.positionSS / _ScreenParams.xy;
// 正交相机的深度 存储在 w 分量中
f.depth = IsOrthographicCamera() ? OrthographicDepthBufferToLinear(positionSS.z) : positionSS.w;
f.bufferDepth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, sampler_point_clamp, f.screenUV);
f.bufferDepth = IsOrthographicCamera() ? OrthographicDepthBufferToLinear(f.bufferDepth) : LinearEyeDepth(f.bufferDepth,_ZBufferParams);
return f;
}