已知,一个坐标系中的顶点坐标可以通过它相对于另一个顶点坐标的偏移来求得。我们可以先知道摄像机在世界坐标下的位置,以及世界空间下该像素相对于摄像机的偏移量,把它们相加就可以得到改像素的世界坐标。
float4 worldPos = _WorldSpaceCameraPos + linearDepth * interpolatedRay;
linearDepth是由深度纹理得到的线性深度值,interpolatedRay是由顶点着色器输出并插值得到的射线,它不仅包含了该像素到摄像机的方向,也包含了距离信息。
怎么求这个interpolatedRay就是重点了。interpolatedRay来源于近裁剪平面的4个角的某个特定向量的插值,这4个向量包含了它们到摄像机的方向和距离信息,最后根据顶点比较偏向哪个角,就用那边的特定向量的线性插值来计算世界坐标。
//近剪裁面中心到top边的垂直距离 float halfHeight = near * Mathf.Tan(fov * 0.5f * Mathf.Deg2Rad); //近剪裁面中心垂直指向左边 Vector3 toRight = cam.transform.right * halfHeight * aspect; //近剪裁面中心垂直指向上边 Vector3 toTop = cam.transform.up * halfHeight; Vector3 topLeft = cam.transform.forward * near - toRight + toTop; float scale = topLeft.magnitude / near;//计算一个depth值转换成真实长度的scale //相机指向近剪裁面的左上角 topLeft.Normalize(); topLeft *= scale; //相机指向近剪裁面的右上角 Vector3 topRight = cam.transform.forward * near + toRight + toTop; topRight.Normalize(); topRight *= scale; //相机指向近剪裁面的右下角 Vector3 bottomRight = cam.transform.forward * near + toRight - toTop; bottomRight.Normalize(); bottomRight *= scale;
各个方向乘以scale是因为通过深度贴图获取的linearDepth只是在z轴上的,而不是两点之间的距离,所以通过这个scale来转换过来,scale的由来(近似三角形):