深度图中存储的值是什么?(深度缓冲Zbuffer的值)
投影矩阵
顶点P的投影变换
顶点P:
(1)观察空间坐标:Pview = (x, y, z, 1)
(2)裁剪空间坐标:Pclip = 投影变换后的坐标,范围为[-w, w]
(3)归一化的设备坐标NDC:Pndc = Pclip / w(齐次除法),范围[-1, 1]
(4)屏幕坐标(二维空间):先将范围转为[0, 1],再乘以相应的屏幕大小
screenX = (ndcX * 0.5 + 0.5) * 屏幕宽度
screenY = (ndcY * 0.5 + 0.5) * 屏幕高度
(5)深度缓冲(Z分量):范围为[0, 1]
d = ndcZ * 0.5 + 0.5
这个d就是深度图中的值。
获取深度图的步骤:
1、设置摄像机的depthTextureMode,告诉摄像机你需要一张深度纹理:
camera.depthTextureMode = DepthTextureMode.Depth;
2、在shader中通过属性 _CameraDepthTexture 获取深度图:
sampler2D _CameraDepthTexture;
3、深度图采样:
1、如果是屏幕后处理,可以直接用uv访问
//i.uv对应了当前像素的纹理坐标
float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv);
//等价于
float depth = tex2D(_CameraDepthTexture, i.uv).r;
//判断是单通道还是多通道深度图,多通道使用编码EncodeFloatRGBA、DecodeFloatRGBA
float depth = UNITY_SAMPLE_DEPTH(tex2D(_CameraDepthTexture, i.uv));
2、使用投影纹理采样
//vertex
o.screenPos = ComputeScreenPos(o.vertex);//o.vertex是裁剪空间的顶点
//fragment
float depth = SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.screenPos));
3、其他方式
float depth = SAMPLE_DEPTH_TEXTURE_LOD(_CameraDepthTexture, UNITY_PROJ_COORD(float4(i.uv,0,0)));
SAMPLE_DEPTH_TEXTURE的源码:
UnityCG.cginc
#define SAMPLE_DEPTH_TEXTURE(sampler, uv) (tex2D(sampler, uv).r)
UNITY_SAMPLE_DEPTH的源码:
#if defined(SHADER_API_PS3)
#define UNITY_SAMPLE_DEPTH(value) (dot((value).wxy, float3(0.996093809371817670572857294849, 0.0038909914428586627756752238080039, 1.5199185323666651467481343000015e-5)))
#elif defined(SHADER_API_FLASH)
#define UNITY_SAMPLE_DEPTH(value) (DecodeFloatRGBA(value))
#else
#define UNITY_SAMPLE_DEPTH(value) (value).r
#endif
ComputeScreenPos的源码:
inline float4 ComputeScreenPos(float4 pos)
{
float4 o = ComputeNonStereoScreenPos(pos);
#if defined(UNITY_SINGLE_PASS_STEREO)
o.xy = TransformStereoScreenSpaceTex(o.xy, pos.w);
#endif
return o;
}
inline float4 ComputeNonStereoScreenPos(float4 pos)
{
float4 o = pos * 0.5f;
o.xy = float2(o.x, o.y*_ProjectionParams.x) + o.w;
o.zw = pos.zw;
return o;
}
ComputeScreenPos返回的值是齐次坐标系下的屏幕坐标值,其范围为[0, w]。
Unity的本意是希望你把该坐标值用作tex2Dproj指令的参数值,tex2Dproj会在对纹理采样前除以w分量。
深度计算
1、在顶点着色器中计算深度值
UnityCG.cginc
#define COMPUTE_EYEDEPTH(o) o = -mul( UNITY_MATRIX_MV, v.vertex ).z
#define COMPUTE_DEPTH_01 -(mul( UNITY_MATRIX_MV, v.vertex ).z * _ProjectionParams.w)
其中,_ProjectionParams.w 是 1 / FarPlane。
符号取反的原因是在Unity的观察空间(View space)中z轴翻转了,摄像机的前向量就是z轴的负方向。这是和OpenGL中不一样的一点。
从上式可知,Unity中的观察线性深度(Eye depth)就是顶点在观察空间(View space)中的z分量,而01线性深度(01 depth)就是观察线性深度通过除以摄像机远平面重新映射到[0,1]区间所得到的值。
2、从深度缓冲中采样得到深度值(屏幕后处理效果)
UnityCG.cginc
// Z buffer to linear 0..1 depth (0 at eye, 1 at far plane)
inline float Linear01Depth( float z )
{
return 1.0 / (_ZBufferParams.x * z + _ZBufferParams.y);
}
// Z buffer to linear depth
inline float LinearEyeDepth( float z )
{
return 1.0 / (_ZBufferParams.z * z + _ZBufferParams.w);
}













网友评论