XNA::ComputeFrustumFromProjection的一些注释

ComputeFrustumFromProjection是通过投影矩阵计算出view space下frustum的函数,Frustum在XNA内用这个结构描述

struct Frustum
{
XMFLOAT3 Origin;
XMFLOAT4 Orientation;
FLOAT RightSlope;
FLOAT LeftSlope;
FLOAT TopSlope;
FLOAT BottomSlope;
FLOAT Near, Far;
}
      其中,view space下frustum的origin是原点,orientation是identity transform,所以,函数主要工作是通过projection matrix计算rightslope,leftslope,topslope,bottomslope,near和far。
      先描述计算rightslope和leftslope方法,这个方法跟计算topslope和bottomslope的方法是一样的。计算rightslope和leftslope的直观方法是通过projection space下的x,0,z平面、far plane、right plane和left plane相交的两个点及projection matrix的inverse matrix,得到这两点在view space下的坐标,然后计算x/y就得到rightslope(leftslope)。
      但问题是,projection space下,这些平面的相交点坐标是多少,不知道。不过,在projection space的下一个frame--normalized device space(coordinate NDC)里,这两个点的坐标是知道的,分别是(1.0,0.0,1.0,1.0)和(-1.0,0.0,1.0,1.0),因为NDC下所有点都在[-1,1]×[-1,1]×[0,1]里面。再观察,由projection space到NDC只是一个scalar*vector的运算(除以w,即乘以1/w),在线性变换的前提下,T(scaler*vector)=scaler*T(vector)。所以,在上述条件支撑下,得到下面的推导:
P_ndc = P_proj / P_proj.w = P_proj / P_view.z;
=> P_proj = P_ndc * P_view.z;
P_view = T_proj_inv(P_proj) = T_proj_inv(P_ndc * P_view.z) = T_proj_inv(P_ndc) * P_view.z;
let P_temp = T_proj_inv(P_ndc);
then
P_view.x = P_temp.x * P_view.z;
P_view.y = P_temp.y * P_view.z;
P_view.z = P_temp.z * P_view.z;
P_view.w = P_temp.w * P_view.z;
then
rightslope = P_view.x / P_view.z = (P_temp.x * P_view.z) / (P_temp.z * P_view.z) = P_temp.x / P_temp.z;
so
rightslope = T_proj_inv(P_ndc).x / T_proj_inv(P_ndc).z;

对应XNA::ComputeFrustumFromProjection代码就是
Points[i] = XMVector4Transform(HomogenousPoints[i], matInverse); // T_proj_inv(P_ndc)
...
Points[0] = Points[0] * XMVectorReciprocal(XMVectorSplatZ(Point[0])); // P_temp.xyzw乘以1/P_temp.z
...
pOut->RightSlope = XMVectorGetX(Points[0]); // 取x

    通过上述计算得到right、left、top和bottom slope,接下来要计算near plane和far plane离原点的距离near和far。在view space下,near和far就是near plane和far plane与z轴交点的z值,与计算slope方法类似,我们通过NDC下的坐标值计算出view space下的z值。NDC下,near和far交点分别是(0.0,0.0,0.0,1.0)、(0.0,0.0,1.0,1.0),观察计算slope方法,可以看出ndc.w和view.z的关系:
P_view = T_proj_inv(P_ndc * P_view.z) = T_proj_inv(P_ndc) * P_view.z;
let P_temp = T_proj_inv(P_ndc);
then
...
P_view.z = P_temp.z * P_view.z;
P_view.w = P_temp.w * P_view.z;
可见,P_temp.z是等于1,又因为当P_view代表是点的时候P_view.w等于1(w等于1表明是点,等于零表明是向量),所以P_temp.w = 1/P_view.z,所以,P_view.z就是1/P_temp.w
对应XNA的代码是
Points[4] = Points[4] * XMVectorReciprocal(XMVectorSplatW(Points[4]));// P_temp.xyzw 乘以 1/P_temp.w即P_view.z
...
pOut->Near = XMVectorGetZ(Points[4]); // 因为P_temp.z等于1,所以乘以1/P_temp.w之后,P_temp.z就是P_view.z

原文地址:https://www.cnblogs.com/rickerliang/p/2792564.html