小祥的BLOG

游戏开发
posts - 7, comments - 14, trackbacks - 0, articles - 0
  IT博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

DirectX下 Viewing Frustum 的详细实现

Posted on 2007-11-15 18:40 小祥 阅读(970) 评论(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

Feedback

# re: DirectX下 Viewing Frustum 的详细实现  回复  更多评论   

2009-12-10 16:32 by 4343
TTCall介绍
TTCall是由新博网络有限公司集即时通讯、网络电话、短信、影视、网络游戏集一体的大型娱乐平台。她操作简单,语音加密,实时计费,话音质量可与传统电话,即可实现低至0.2元拨打全国的固话、小灵通与手机。

网内语音服务
使用TTCall可以与您的好友网内免费通话。全球无漫游拨打好友小号!

语音稳定清晰
超一流语音通话,让您享受清晰的通话质量,卓越音质,畅享沟通!

录音功能
通话过程中,您可以点击“录音”按钮进行通话录音。

变声功能:
绝对独家“魔音”功能,“男生变女声、女生变男声”。

网络电视:
上百家娱乐、新闻电视台扑面而至。

精彩大片、网络游戏游戏
通过TTCall您可以观看大片,精神不容错过,同时可以玩热门游戏。

TTCall网址:http://ttcall.tti5.com
软件下载:http://down.tti5.com
只有注册用户登录后才能发表评论。