具体在书p275页
这里为啥需要除D.w呢。
首先我们得到的NDC的坐标是已经归一化的,但是CurrenViewProjectionMatrix的作用,是把世界空间转化为尚未归一化的裁剪空间。
这里看书上能够得知CurrenViewProjectionInverseMatrix是如何得到的
调动了camera.worldToCameraMatrix和camera.projectionMatrix
然后查找Unity的函数得知变换得到的顶点坐标是没有归一化的
Camera.projectionMatrix.
static Matrix4x4 PerspectiveOffCenter(float left, float right, float bottom, float top, float near, float far)
{
float x = 2.0F * near / (right - left);
float y = 2.0F * near / (top - bottom);
float a = (right + left) / (right - left);
float b = (top + bottom) / (top - bottom);
float c = -(far + near) / (far - near);
float d = -(2.0F * far * near) / (far - near);
float e = -1.0F;
Matrix4x4 m = new Matrix4x4();
m[0, 0] = x;
m[0, 1] = 0;
m[0, 2] = a;
m[0, 3] = 0;
m[1, 0] = 0;
m[1, 1] = y;
m[1, 2] = b;
m[1, 3] = 0;
m[2, 0] = 0;
m[2, 1] = 0;
m[2, 2] = c;
m[2, 3] = d;
m[3, 0] = 0;
m[3, 1] = 0;
m[3, 2] = e;
m[3, 3] = 0;
return m;
}
}
即
ps:unity是左手系,所以z轴需要取反,也就是第三行多乘一个负号。
所以根据书上的描述我们可以得到等式
-
worldPos * CurrenViewProjectionMatrix / w = NDC
把worldPos看成(x, y , z ,1)
-
即 (x / w, y / w, z / w, 1 / w) = NDC * CurrenViewProjectionInverseMatrix
//H是NDC坐标,因为uv是纹理坐标
//在屏幕后处理中,像素的纹理坐标*2 - 1 得到的就是NDC坐标【0,1】->【-1,1】
float H = float4(i.uv.x * 2 - 1, i.uv.y * 2 - 1, d * 2 - 1, 1);
//D就是NDC * CurrenViewProjectionInverseMatrix
float4 D = mul(_CurrentViewProjectionInverseMatrix,H);
float4 worldPos = D / D.w;
也就是前两行代码计算得到的结果
- 可得:worldPos / worldPos.w 即为( x / w, y / w, z / w, 1 / w) / (1 / w)
- 即为(x , y , z ,1)
这里并不是因为1才能得到正确结果,即使原本不是1,除以w也能得到。
这里关于屏幕坐标和像素坐标的关系不做解释,书上已经说得很清楚了。