深度图的解释
在坐标变换中,顶点从裁剪空间到屏幕空间中间有两个步骤,一个是NDC,一个是屏幕映射。
NDC
在NDC之前,x,y,z,w的值跟Near和far有关,w的范围就是[Near,Far]。
经过NDC,即用w除以x,y,z,此时x,y,z的值范围是[-1,1],w是1.(但在OpenGl中,z的范围是[0,1])。
z就是深度值。
储存到纹理中映射一下:d=z*0.5+0.5
屏幕映射
经过NDC后,在根据屏幕像素来把x,y映射到屏幕上。
映射方法:
screen_x = clip_x * pixelWidth/ 2 *clipx+pixelWidth/ 2
screen_y = clip_y * pixelHeight/ 2 *clipx+pixelHeight/ 2
裁剪空间的坐标(clip_x,clip_y)
屏幕像素(pixelWidth,pixelHeight)
每个顶点的原始的Z值是视空间的深度,转化到屏幕空间后,在屏幕空间的深度与1/z成正比才可以在屏幕空间逐像素地进行插值进而获得屏幕上任意一点像素的屏幕空间深度值。
使用时要把屏幕空间的深度再转换回原始的视空间深度。
使用深度图
v2f
分配一个储存器存储
float4 projPos : TEXCOORD5;
顶点着色器
计算在屏幕空间的点,获取深度值z
o.vertex = UnityObjectToClipPos(v.vertex);
o.projPos = ComputeScreenPos (o.vertex);//计算顶点在屏幕空间的位置(没有进行透视除法)
COMPUTE_EYEDEPTH(o.projPos.z);//计算顶点距离相机的距离
//实际的计算#define COMPUTE_EYEDEPTH(o) o = -UnityObjectToViewPos( v.vertex ).z
//把顶点转化到视空间,取z值的负数就是真正的视空间距离了。
声明深度图
sampler2D _CameraDepthTexture;
片元着色器
//根据上面的屏幕空间位置,进行透视采样深度图(tex2dproj,即带有透视除法的采样,相当于tex2d(xy/w)),
//得到当前像素对应在屏幕深度图的深度,并转化到视空间,线性化(深度图中已有的不透明对象的深度)
float sceneZ = LinearEyeDepth (SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos)));
//本像素点在视空间真正的距离(粒子本身的深度)
float partZ = i.projPos.z;
//计算二者的深度差,该值越小,说明越近穿插
float fade = saturate (_InvFade * (sceneZ-partZ));
LinearEyeDepth和Linear01Depth
原函数如下:
// 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);
}
_ZBufferParams的含义如下:
float4 _ZBufferParams (0-1 range):
// Values used to linearize the Z buffer
x = 1-far/near
y = far/near
z = x/far
w = y/far
// or in case of a reversed depth buffer (UNITY_REVERSED_Z is 1)
//有些平台使用了Reverse-Z
x = -1+