提取视锥体平面(Gribb&Hartmann方法)

15
我一直在努力理解Gribb/Hartmann提取视锥体平面的方法,但是一直没有成功。我想要建立一个摄像机视锥体来剔除我的场景。
我使用右手坐标系中的列主矩阵(OpenGL风格 - 我正在使用C#和Playstation Mobile,但数学应该是相同的)。
我想要在世界空间中获取我的平面,所以我从视图-投影矩阵中构建我的视锥体(也就是投影矩阵*视图矩阵)。视图矩阵是相机世界变换的逆矩阵。
问题是,无论我如何调整,似乎都无法得到正确的视锥体。我认为可能是我漏掉了一些显而易见的东西。
如果我在仍然看着z轴的情况下向左或向右移动相机,我的平面法线会改变,使它们始终指向场景的原点 - 这让我觉得它们不在世界空间中...

昨天我碰到了同样的问题。我有一种感觉,那篇论文中的OpenGL代码可能是错误的,或者至少我使用DirectX代码并没有出现问题,尽管我正在使用OpenGL。我用点云可视化平面,现在它们看起来是正确的。你试过吗? - PolyMesh
我有完全相同的问题。我已经实现了DirectX样式段落中描述的算法,因为我正在使用行主矩阵查询。我无法使正确的剔除工作。但是我注意到,如果要被剔除的对象恰好位于世界中心(0, 0, 0),则剔除可以完美地工作。@DAVco,你能解决这个问题吗? - Haeri
2个回答

14

使用Gribb/Hartmann方法,可以提取投影矩阵的平面如下(列优先):

void extract_planes_from_projmat(
        const float mat[4][4],
        float left[4], float right[4],
        float bottom[4], float top[4],
        float near[4], float far[4])
{
    for (int i = 4; i--; ) { left[i]   = mat[i][3] + mat[i][0]; }
    for (int i = 4; i--; ) { right[i]  = mat[i][3] - mat[i][0]; }
    for (int i = 4; i--; ) { bottom[i] = mat[i][3] + mat[i][1]; }
    for (int i = 4; i--; ) { top[i]    = mat[i][3] - mat[i][1]; }
    for (int i = 4; i--; ) { near[i]   = mat[i][3] + mat[i][2]; }
    for (int i = 4; i--; ) { far[i]    = mat[i][3] - mat[i][2]; }
}

其中mat4是投影矩阵和模型视图矩阵的乘积。

参见:

注意:如果矩阵的分量没有归一化,并且您需要一个 Hessian 正规形式的平面,那么您需要对结果平面进行归一化处理。

在进行任意变换时,您仍需要对平面进行归一化。 - Matthias
这只有在使用这些飞机的任何代码需要对它们进行归一化时才会发生,许多飞机操作不需要这样做,即便如此,感谢您的提示-已在答案中注意到。 - ideasman42
你是对的,不是所有操作都需要规范化平面。然而,如果你去掉一些自由度,代码会更加健壮。 - Matthias
1
当然你可能想要归一化,但在某些情况下不归一化也可以 - 因为归一化可能是昂贵的。在答案中指出,如果需要归一化,则可以执行归一化。 - ideasman42

2
缺失的部分:
comboMatrix = projection_matrix * Matrix4_Transpose(modelview_matrix)

那么对于OpenGL的世界空间截锥体平面提取,正如Gribb/Hartmann方法中所述:

p_planes[0].a = comboMatrix._41 + comboMatrix._11;
p_planes[0].b = comboMatrix._42 + comboMatrix._12;
p_planes[0].c = comboMatrix._43 + comboMatrix._13;
p_planes[0].d = comboMatrix._44 + comboMatrix._14;
// Right clipping plane
p_planes[1].a = comboMatrix._41 - comboMatrix._11;
p_planes[1].b = comboMatrix._42 - comboMatrix._12;
p_planes[1].c = comboMatrix._43 - comboMatrix._13;
p_planes[1].d = comboMatrix._44 - comboMatrix._14;
// Top clipping plane
p_planes[2].a = comboMatrix._41 - comboMatrix._21;
p_planes[2].b = comboMatrix._42 - comboMatrix._22;
p_planes[2].c = comboMatrix._43 - comboMatrix._23;
p_planes[2].d = comboMatrix._44 - comboMatrix._24;
// Bottom clipping plane
p_planes[3].a = comboMatrix._41 + comboMatrix._21;
p_planes[3].b = comboMatrix._42 + comboMatrix._22;
p_planes[3].c = comboMatrix._43 + comboMatrix._23;
p_planes[3].d = comboMatrix._44 + comboMatrix._24;
// Near clipping plane
p_planes[4].a = comboMatrix._41 + comboMatrix._31;
p_planes[4].b = comboMatrix._42 + comboMatrix._32;
p_planes[4].c = comboMatrix._43 + comboMatrix._33;
p_planes[4].d = comboMatrix._44 + comboMatrix._34;
// Far clipping plane
p_planes[5].a = comboMatrix._41 - comboMatrix._31;
p_planes[5].b = comboMatrix._42 - comboMatrix._32;
p_planes[5].c = comboMatrix._43 - comboMatrix._33;
p_planes[5].d = comboMatrix._44 - comboMatrix._34;

这些平面现在处于世界空间,可以用来对世界空间中的物体进行视锥体裁剪。

for(int i = 0; i < 6; i++)
{
    var dist = dot3(world_space_point.xyz, p_planes[i].xyz) + p_planes[i].d + sphere_radius;
    if(dist < 0) return false; // sphere culled
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接