文章

从MVP矩阵快速提取视锥平面方程

本文提出了一种方法,可以直接从世界、观察以及投影矩阵中计算出Viewing Frustum的六个面。

这个算法不仅快速准确,而且具有普适性,他可以让我们从相机空间、世界空间或者物体空间快速确定Frustum planes。

我们先从投影矩阵开始,首先我们假设世界矩阵和观察矩阵都是单位矩阵。这就意味着相机位于世界坐标系下的原点,并且朝向Z轴的正方向。

至此,我们现在已经可以直接从投影矩阵中提取视锥体的左裁剪平面方程。这里我们需要注意的是,得到的平面方程式没有归一化的(平面的法线向量不是单位向量),而且平面的法线方向是指向平面内部的。这就是说,如果要判断 v 在左裁剪平面空间内部,那么必须满足 ax + by + cz + d > 0

重复以上几步,可推导出到其他的几个裁剪面,具体见下表

到目前为止,我们都是假设世界矩阵( world )和观察矩阵( view )都是单位化了的矩阵。但是,本算法并不受这种条件的限制,而是在任何条件下都能使用,且有以下结论:

  1. 如果矩阵 M 等于投影矩阵 P ( M = P ),那么算法给出的裁剪面是在相机空间(camera space)
  2. 如果矩阵 M 等于观察矩阵 V 和投影矩阵 P 的组合( M = V * P ),那么算法给出的裁剪面是在世界空间(world space)
  3. 如果矩阵 M 等于世界矩阵 W,观察矩阵 V 和投影矩阵 P 的组合( M = W* V * P ),那么算法给出的裁剪面是在物体空间(object space)

下面给出OpenGL中的代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
struct Matrix4x4
{
 // The elements of the 4x4 matrix are stored in
 // column-major order (see "OpenGL Programming Guide",
 // 3rd edition, pp 106, glLoadMatrix).
 float _11, _21, _31, _41;
 float _12, _22, _32, _42;
 float _13, _23, _33, _43;
 float _14, _24, _34, _44;
};

void ExtractPlanesGL(
 Plane * p_planes,
 const Matrix4x4 & comboMatrix,
 bool normalize)
{
     // Left clipping plane
     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;

    // Normalize the plane equations, if requested
    if (normalize == true)
    {
        NormalizePlane(p_planes[0]);
        NormalizePlane(p_planes[1]);
        NormalizePlane(p_planes[2]);
        NormalizePlane(p_planes[3]);
        NormalizePlane(p_planes[4]);
        NormalizePlane(p_planes[5]);
    }
}

DirectX中平面方程


Ref

http://www8.cs.umu.se/kurser/5DV180/VT18/lab/plane_extraction.pdf

本文由作者按照 CC BY 4.0 进行授权