需求:根据当前像素的Depth计算出其View空间的Position
先说一种惯性思维的方法:既然知道depth是怎么算出来的,那么进行逆运算回去不就得到position了?
先说说depth是怎么出来的:
Vertexshader:
output.position=mul(input.postion,matWorldViewProject); output.depth.xy=output.position.zw; |
Pixelshader(输出z/w):
returninput.depth.x/input.depth.y; |
那么,逆运算回去就很直接了(input.uv是全屏矩形的纹理坐标):
floatz=tex2D(DepthSampler,input.uv); //transformtoprojectionspace floatx=input.uv.x*2-1; floaty=(1-input.uv.y)*2-1; float4vProjectedPos=float4(x,y,z,1); //transformtoviewspace float4vPosition=mul(vProjectedPos,matInvProject); return(vPosition.xyz/vPosition.w); |
那么这样做有什么缺点呢?
lz/w是非线性分布的,经过RTT后再变换回去会有精度上的损失
l计算量有点大,要知道PS里的每个指令都是很宝贵的.
下面说说另一种非常快的算法,而且也可以解决精度问题.先看看摄像机视锥体的抽象形式:
从摄像机位置到远裁剪面发射一条射线,那么,对于可见的任意一点,有这么个关系:
vPositionView=vViewRayDir*fLinearDepth; |
其中,fLinearDepth代表规格化的Z,它是线性分布的,即:
fLinearDepth=vPositionView.z/fFarClipDist; |
剩下的,就是这个屏幕射线vViewRayDir从哪来的问题了.
我们知道,在View空间,摄像机位置是(0,0,0).那么,对于每条射线的方向,等价于射线与远裁剪面的交点坐标.即:
vViewRayDir=float3(fFarClipX,fFarClipY,fFarClipDist); |
远裁剪面上的4个顶点坐标我们是可以算出来的,就是Frustum中的四个顶点.如果我们把这四个顶点坐标写入全屏矩形的顶点坐标中,然后在VS中输出,那么在PS中得到的就是已经插值好的射线方向了!
整理一下整个思路:
1.把vPositionView.z/fFarClipDist输出到RTT,这里因为是线性分布的,在精度允许的前提下可以进行压缩
2.从RTT里得到fLinearDepth,从VS_OUTPUT出的寄存器里得到已经插值好的vViewRayDir.xy,vViewRayDir.z就是fFarClipDist,Position的重建只需要一句计算就可以得到:
vPositionView=vViewRayDir*fLinearDepth; |
Reference(要):http://mynameismjp.wordpress.com/2009/03/10/reconstructing-position-from-depth/