美文网首页
Unity中的深度图

Unity中的深度图

作者: LEO_青蛙 | 来源:发表于2020-06-20 16:25 被阅读0次

深度图中存储的值是什么?(深度缓冲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);
}

相关文章

网友评论

      本文标题:Unity中的深度图

      本文链接:https://www.haomeiwen.com/subject/ipcgxktx.html