Posted on 2007-11-15 18:40
小祥 阅读(969)
评论(1) 编辑 收藏 引用 所属分类:
D3D
本文大部分内容翻译自Gil Gribb和Klaus Hartmann合写的《Fast Extraction of Viewing Frustum Planes from the World-View-Projection Matrix》这篇文章,有兴趣的朋友可以搜索看下原文,里面DirectX下和OpenGL下的实现过程都说的很清楚,这里只说DirectX部分。
这里介绍的算法,可以直接从世界、观察以及投影矩阵中计算出Viewing Frustum的六个面。它快速,准确,并且允许我们在相机空间(camera space)、世界空间(world space)或着物体空间(object space)快速确定Frustum planes。
我们先仅仅从投影矩阵(project)开始,也就是假设世界矩阵(world)和观察矩阵(view)都是单位化了的矩阵。这就意味着相机位于世界坐标系下的原点,并且朝向Z轴的正方向。
定义一个顶点v(x y z w=1)和一个4*4的投影矩阵M=m(i,j),然后我们使用该矩阵M对顶点v进行转换,转换后的顶点为v'= (x' y' z' w'),可以写成这样:
转换后,viewing frustum实际上就变成了一个与轴平行的盒子,如果顶点 v' 在这个盒子里,那么转换前的顶点 v 就在转换前的viewing frustum里。在Direct3D下,如果下面的几个不等式都成立的话,那么 v' 就在这个盒子里。
-w' < x' < w'
-w' < y' < w'
0 < z' < w'
可得到如下结论,列在下表里:
现在假设,我们要测试x'是否在左半空间内,根据上表,也就是判断 -w' < x' 是否成立。用我们开始提到的信息,可将不等式写成如下形式:
-( v * col4 ) < ( v * col1 )
即:
0 < ( v * col4 ) + ( v * col1 )
得到最后形式:
0 < v * ( col1 + col4 )
写到这里,其实已经等于描绘出了转换前的viewing frustum的左裁剪面的平面方程:
x * ( m14 + m11 ) + y * ( m24 + m21 ) + z * ( m34 + m31) + w * ( m44 + m41 ) = 0
当W = 1,我们可简单成如下形式:
x * ( m14 + m11 ) + y * ( m24 + m21 ) + z * ( m34 + m31) + ( m44 + m41 ) = 0
这就给出了一个基本平面方程:
ax + by + cz + d = 0
其中,a = ( m14 + m11 ) , b = ( m24 + m21 ), c = ( m34 + m31) , d = ( m44 + m41 )
ok,到这里左裁剪面就得到了。重复以上几步,可推导出到其他的几个裁剪面,具体见下表:
需要注意的是:最终得到的平面方程都是没有单位化的(平面的法向量不是单位向量),并且法向量指向空间的内部。这就是说,如果要判断 v 在空间内部,那么6个面必须都满足ax + by + cz + d > 0
到目前为止,我们都是假设世界矩阵( world )和观察矩阵( view )都是单位化了的矩阵。但是,本算法并不想受这种条件的限制,而是希望可以在任何条件下都能使用。实际上,这也并不复杂,并且简单得令人难以置信。如果你仔细想一下就会立刻明白了,所以我们不再对此进行详细解释了,下面给出3个结论:
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)
好,到此为止,理论知识就全部说完了,下面给出具体的实现代码:
1
===============================
Frustum.h
==============================
2
3
#ifndef __FrustumH__
4
#define
__FrustumH__
5
6
#include
<
d3dx9.h
>
7
8
class
Frustum
9
{
10
public
:
11
Frustum();
12
~
Frustum();
13
//
Call this every time the camera moves to update the frustum
14
void
CalculateFrustum( D3DXMATRIX ViewMatrix, D3DXMATRIX ProjectMatrix );
15
//
This takes a 3D point and returns TRUE if it's inside of the frustum
16
bool
PointInFrustum( D3DXVECTOR3 Point );
17
private
:
18
//
This holds the A B C and D values for each side of our frustum.
19
D3DXPLANE FrustumPlane[
6
];
20
}
;
21
#endif
//
__FrustumH
22
23
=============================
Frustum.cpp
============================
24
25
#include
"
Frustum.h
"
26
#include
<
D3dx9math.h
>
27
28
enum
FrustumSide
{ RIGHT, LEFT, BOTTOM, TOP, FRONT, BACK }
;
29
30
Frustum::Frustum()
31
{
32
}
33
Frustum::
~
Frustum()
34
{
35
}
36
/**/
/////////////////////////////////
CALCULATE FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
37
///
//
38
///
// This extracts our frustum from the projection and view matrix.
39
///
//
40
/////////////////////////////////
CALCULATE FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
41
void
Frustum::CalculateFrustum( D3DXMATRIX ViewMatrix, D3DXMATRIX ProjectMatrix )
42
{
43
D3DXMATRIX ComboMatrix;
44
D3DXMatrixMultiply(
&
ComboMatrix,
&
ViewMatrix,
&
ProjectMatrix );
45
//
right clipping plane
46
FrustumPlane[RIGHT].a
=
ComboMatrix._14
-
ComboMatrix._11;
47
FrustumPlane[RIGHT].b
=
ComboMatrix._24
-
ComboMatrix._21;
48
FrustumPlane[RIGHT].c
=
ComboMatrix._34
-
ComboMatrix._31;
49
FrustumPlane[RIGHT].d
=
ComboMatrix._44
-
ComboMatrix._41;
50
51
//
normalize
52
D3DXPlaneNormalize(
&
FrustumPlane[RIGHT],
&
FrustumPlane[RIGHT] );
53
54
//
left clipping plane
55
FrustumPlane[LEFT].a
=
ComboMatrix._14
+
ComboMatrix._11;
56
FrustumPlane[LEFT].b
=
ComboMatrix._24
+
ComboMatrix._21;
57
FrustumPlane[LEFT].c
=
ComboMatrix._34
+
ComboMatrix._31;
58
FrustumPlane[LEFT].d
=
ComboMatrix._44
+
ComboMatrix._41;
59
60
//
normalize
61
D3DXPlaneNormalize(
&
FrustumPlane[LEFT],
&
FrustumPlane[LEFT] );
62
63
//
bottom clipping plane
64
FrustumPlane[BOTTOM].a
=
ComboMatrix._14
+
ComboMatrix._12;
65
FrustumPlane[BOTTOM].b
=
ComboMatrix._24
+
ComboMatrix._22;
66
FrustumPlane[BOTTOM].c
=
ComboMatrix._34
+
ComboMatrix._32;
67
FrustumPlane[BOTTOM].d
=
ComboMatrix._44
+
ComboMatrix._42;
68
69
//
normalize
70
D3DXPlaneNormalize(
&
FrustumPlane[BOTTOM],
&
FrustumPlane[BOTTOM] );
71
72
//
top clipping plane
73
FrustumPlane[TOP].a
=
ComboMatrix._14
-
ComboMatrix._12;
74
FrustumPlane[TOP].b
=
ComboMatrix._24
-
ComboMatrix._22;
75
FrustumPlane[TOP].c
=
ComboMatrix._34
-
ComboMatrix._32;
76
FrustumPlane[TOP].d
=
ComboMatrix._44
-
ComboMatrix._42;
77
78
//
normalize
79
D3DXPlaneNormalize(
&
FrustumPlane[TOP],
&
FrustumPlane[TOP] );
80
//
near clipping plane
81
FrustumPlane[FRONT].a
=
ComboMatrix._14
+
ComboMatrix._13;
82
FrustumPlane[FRONT].b
=
ComboMatrix._24
+
ComboMatrix._23;
83
FrustumPlane[FRONT].c
=
ComboMatrix._34
+
ComboMatrix._33;
84
FrustumPlane[FRONT].d
=
ComboMatrix._44
+
ComboMatrix._43;
85
86
//
normalize
87
D3DXPlaneNormalize(
&
FrustumPlane[FRONT],
&
FrustumPlane[FRONT] );
88
89
//
far clipping plane
90
FrustumPlane[BACK].a
=
ComboMatrix._14
-
ComboMatrix._13;
91
FrustumPlane[BACK].b
=
ComboMatrix._24
-
ComboMatrix._23;
92
FrustumPlane[BACK].c
=
ComboMatrix._34
-
ComboMatrix._33;
93
FrustumPlane[BACK].d
=
ComboMatrix._44
-
ComboMatrix._43;
94
95
//
normalize
96
D3DXPlaneNormalize(
&
FrustumPlane[BACK],
&
FrustumPlane[BACK] );
97
}
98
99
/**/
/////////////////////////////////
POINT IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
100
///
//
101
///
// This determines if a point is inside of the frustum
102
///
//
103
/////////////////////////////////
POINT IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
104
bool
Frustum::PointInFrustum( D3DXVECTOR3 Point )
105
{
106
for
(
int
i
=
0
; i
<
6
; i
++
)
107
{
108
float
x
=
D3DXPlaneDotCoord(
&
FrustumPlane[i],
&
Point );
109
if
( x
<
0
)
110
return
false
;
111
}
112
//
The point was inside of the frustum (In front of ALL the sides of the frustum)
113
return
true
;
114
}
115
116