原文地址:http://www.linuxgraphics.cn/graphics/opengl_view_frustum_culling.html

背景

视锥体(frustum),是指场景中摄像机的可见的一个锥体范围。它有上、下、左、右、近、远,共6个面组成。在视锥体内的景物可见,反之则不可见。为提高性能,只对其中与视锥体有交集的对象进行绘制。

视锥体

我们计算出视锥体六个面的空间平面方程,将点坐标分别代入六个面的平面方程做比较,则可以判断点是否在视锥体内。

空间平面方程可表示为:

  1. Ax+By+Cz=0

对于点(x1, y1, z1),有

  1. Ax1+By1+Cz1 = 0,则点在平面上;
  2. Ax1+By1+Cz1 < 0,则点在平面的一侧;
  3. Ax1+By1+Cz1 = 0,则点在平面的另一侧;

求视锥平面系数1

这里介绍的算法,可以直接从世界、观察以及投影矩阵中计算出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里。在OpenGL下,如果下面的几个不等式都成立的话,那么 v' 就在这个盒子里。

  1. -w' < x' < w'
  2. -w' < y' < w'
  3. -w' < z' < w'

可得到如下结论,列在下表里:

我们假设现在想测试 x' 是否在左半边空间,只需判断

  1. -w < x'

用上面的信息,等式我们可以写成:

  1. −(v row4 ) < (v row1 )
  2.  
  3. 0 < (v row4 ) + (v row1 )
  4.  
  5. 0 < v (row4 + row1 )

写到这里,其实已经等于描绘出了转换前的viewing frustum的左裁剪面的平面方程:

  1. x(m41 + m11) + y(m42 + m12) + z(m43 + m13) + w(m44 + m14) = 0

当W = 1,我们可简单成如下形式:

  1. x(m41 + m11) + y(m42 + m12) + z(m43 + m13) + (m44 + m14) = 0

这就给出了一个基本平面方程:

  1. ax + by + cz + d = 0

其中,a = ( m41 + m11) , b = ( m42 + m12 ), c = ( m43 + m13) , d = ( m44 + m14 )

到这里左裁剪面就得到了。重复以上几步,可推导出到其他的几个裁剪面,具体见参考文献1.

需要注意的是:最终得到的平面方程都是没有单位化的(平面的法向量不是单位向量),并且法向量指向空间的内部。这就是说,如果要判断 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)

判断节点是否在视锥内

通过各种包围体方法求出近似包围体,对包围体上的各个点对视锥六个面作判断,存在以下三种情况:

  • 如果所有顶点都在视锥范围内,则待判区域一定在视锥范围内;
  • 如果只有部分顶点在视锥范围内,则待判区域与视锥体相交,我们同样视为可见;
  • 如果所有顶点都不在视锥范围内,那么待判区域很可能不可见了,但有一种情况例外,就是视锥体在长方体以内,这种情况我们要加以区分。

基于OpenGL实现

  1. float g_frustumPlanes[][];
  2.  
  3. void calculateFrustumPlanes( void )
  4. {
  5. float p[]; // projection matrix
  6. float mv[]; // model-view matrix
  7. float mvp[]; // model-view-projection matrix
  8. float t;
  9.  
  10. glGetFloatv( GL_PROJECTION_MATRIX, p );
  11. glGetFloatv( GL_MODELVIEW_MATRIX, mv );
  12.  
  13. //
  14. // Concatenate the projection matrix and the model-view matrix to produce
  15. // a combined model-view-projection matrix.
  16. //
  17.  
  18. mvp[ ] = mv[ ] * p[ ] + mv[ ] * p[ ] + mv[ ] * p[ ] + mv[ ] * p[];
  19. mvp[ ] = mv[ ] * p[ ] + mv[ ] * p[ ] + mv[ ] * p[ ] + mv[ ] * p[];
  20. mvp[ ] = mv[ ] * p[ ] + mv[ ] * p[ ] + mv[ ] * p[] + mv[ ] * p[];
  21. mvp[ ] = mv[ ] * p[ ] + mv[ ] * p[ ] + mv[ ] * p[] + mv[ ] * p[];
  22.  
  23. mvp[ ] = mv[ ] * p[ ] + mv[ ] * p[ ] + mv[ ] * p[ ] + mv[ ] * p[];
  24. mvp[ ] = mv[ ] * p[ ] + mv[ ] * p[ ] + mv[ ] * p[ ] + mv[ ] * p[];
  25. mvp[ ] = mv[ ] * p[ ] + mv[ ] * p[ ] + mv[ ] * p[] + mv[ ] * p[];
  26. mvp[ ] = mv[ ] * p[ ] + mv[ ] * p[ ] + mv[ ] * p[] + mv[ ] * p[];
  27.  
  28. mvp[ ] = mv[ ] * p[ ] + mv[ ] * p[ ] + mv[] * p[ ] + mv[] * p[];
  29. mvp[ ] = mv[ ] * p[ ] + mv[ ] * p[ ] + mv[] * p[ ] + mv[] * p[];
  30. mvp[] = mv[ ] * p[ ] + mv[ ] * p[ ] + mv[] * p[] + mv[] * p[];
  31. mvp[] = mv[ ] * p[ ] + mv[ ] * p[ ] + mv[] * p[] + mv[] * p[];
  32.  
  33. mvp[] = mv[] * p[ ] + mv[] * p[ ] + mv[] * p[ ] + mv[] * p[];
  34. mvp[] = mv[] * p[ ] + mv[] * p[ ] + mv[] * p[ ] + mv[] * p[];
  35. mvp[] = mv[] * p[ ] + mv[] * p[ ] + mv[] * p[] + mv[] * p[];
  36. mvp[] = mv[] * p[ ] + mv[] * p[ ] + mv[] * p[] + mv[] * p[];
  37.  
  38. //
  39. // Extract the frustum's right clipping plane and normalize it.
  40. //
  41.  
  42. g_frustumPlanes[][] = mvp[ ] - mvp[ ];
  43. g_frustumPlanes[][] = mvp[ ] - mvp[ ];
  44. g_frustumPlanes[][] = mvp[] - mvp[ ];
  45. g_frustumPlanes[][] = mvp[] - mvp[];
  46.  
  47. t = (float) sqrt( g_frustumPlanes[][] * g_frustumPlanes[][] +
  48. g_frustumPlanes[][] * g_frustumPlanes[][] +
  49. g_frustumPlanes[][] * g_frustumPlanes[][] );
  50.  
  51. g_frustumPlanes[][] /= t;
  52. g_frustumPlanes[][] /= t;
  53. g_frustumPlanes[][] /= t;
  54. g_frustumPlanes[][] /= t;
  55.  
  56. //
  57. // Extract the frustum's left clipping plane and normalize it.
  58. //
  59.  
  60. g_frustumPlanes[][] = mvp[ ] + mvp[ ];
  61. g_frustumPlanes[][] = mvp[ ] + mvp[ ];
  62. g_frustumPlanes[][] = mvp[] + mvp[ ];
  63. g_frustumPlanes[][] = mvp[] + mvp[];
  64.  
  65. t = (float) sqrt( g_frustumPlanes[][] * g_frustumPlanes[][] +
  66. g_frustumPlanes[][] * g_frustumPlanes[][] +
  67. g_frustumPlanes[][] * g_frustumPlanes[][] );
  68.  
  69. g_frustumPlanes[][] /= t;
  70. g_frustumPlanes[][] /= t;
  71. g_frustumPlanes[][] /= t;
  72. g_frustumPlanes[][] /= t;
  73.  
  74. //
  75. // Extract the frustum's bottom clipping plane and normalize it.
  76. //
  77.  
  78. g_frustumPlanes[][] = mvp[ ] + mvp[ ];
  79. g_frustumPlanes[][] = mvp[ ] + mvp[ ];
  80. g_frustumPlanes[][] = mvp[] + mvp[ ];
  81. g_frustumPlanes[][] = mvp[] + mvp[];
  82.  
  83. t = (float) sqrt( g_frustumPlanes[][] * g_frustumPlanes[][] +
  84. g_frustumPlanes[][] * g_frustumPlanes[][] +
  85. g_frustumPlanes[][] * g_frustumPlanes[][] );
  86.  
  87. g_frustumPlanes[][] /= t;
  88. g_frustumPlanes[][] /= t;
  89. g_frustumPlanes[][] /= t;
  90. g_frustumPlanes[][] /= t;
  91.  
  92. //
  93. // Extract the frustum's top clipping plane and normalize it.
  94. //
  95.  
  96. g_frustumPlanes[][] = mvp[ ] - mvp[ ];
  97. g_frustumPlanes[][] = mvp[ ] - mvp[ ];
  98. g_frustumPlanes[][] = mvp[] - mvp[ ];
  99. g_frustumPlanes[][] = mvp[] - mvp[];
  100.  
  101. t = (float) sqrt( g_frustumPlanes[][] * g_frustumPlanes[][] +
  102. g_frustumPlanes[][] * g_frustumPlanes[][] +
  103. g_frustumPlanes[][] * g_frustumPlanes[][] );
  104.  
  105. g_frustumPlanes[][] /= t;
  106. g_frustumPlanes[][] /= t;
  107. g_frustumPlanes[][] /= t;
  108. g_frustumPlanes[][] /= t;
  109.  
  110. //
  111. // Extract the frustum's far clipping plane and normalize it.
  112. //
  113.  
  114. g_frustumPlanes[][] = mvp[ ] - mvp[ ];
  115. g_frustumPlanes[][] = mvp[ ] - mvp[ ];
  116. g_frustumPlanes[][] = mvp[] - mvp[];
  117. g_frustumPlanes[][] = mvp[] - mvp[];
  118.  
  119. t = (float) sqrt( g_frustumPlanes[][] * g_frustumPlanes[][] +
  120. g_frustumPlanes[][] * g_frustumPlanes[][] +
  121. g_frustumPlanes[][] * g_frustumPlanes[][] );
  122.  
  123. g_frustumPlanes[][] /= t;
  124. g_frustumPlanes[][] /= t;
  125. g_frustumPlanes[][] /= t;
  126. g_frustumPlanes[][] /= t;
  127.  
  128. //
  129. // Extract the frustum's near clipping plane and normalize it.
  130. //
  131.  
  132. g_frustumPlanes[][] = mvp[ ] + mvp[ ];
  133. g_frustumPlanes[][] = mvp[ ] + mvp[ ];
  134. g_frustumPlanes[][] = mvp[] + mvp[];
  135. g_frustumPlanes[][] = mvp[] + mvp[];
  136.  
  137. t = (float) sqrt( g_frustumPlanes[][] * g_frustumPlanes[][] +
  138. g_frustumPlanes[][] * g_frustumPlanes[][] +
  139. g_frustumPlanes[][] * g_frustumPlanes[][] );
  140.  
  141. g_frustumPlanes[][] /= t;
  142. g_frustumPlanes[][] /= t;
  143. g_frustumPlanes[][] /= t;
  144. g_frustumPlanes[][] /= t;
  145.  
  146. }
  147.  
  148. bool isBoundingSphereInFrustum( float x, float y, float z)
  149. {
  150. for( int i = ; i < ; ++i )
  151. {
  152. if( g_frustumPlanes[i][] * x +
  153. g_frustumPlanes[i][] * y +
  154. g_frustumPlanes[i][] * z +
  155. g_frustumPlanes[i][] <= )
  156. return false;
  157. }
  158.  
  159. return true;
  160. }

视锥体(frustum)裁剪的更多相关文章

  1. DirectX11 With Windows SDK--20 硬件实例化与视锥体裁剪

    前言 这一章将了解如何在DirectX 11利用硬件实例化技术高效地绘制重复的物体,以及使用视锥体裁剪技术提前将位于视锥体外的物体进行排除. 在此之前需要额外了解的章节如下: 章节回顾 18 使用Di ...

  2. 齐次坐标概念&&透视投影变换推导

    http://daehgib.blog.163.com/blog/static/1861071422011579551134/ 透视投影是3D固定流水线的重要组成部分,是将相机空间中的点从视锥体(fr ...

  3. 透视投影(Perspective Projection)变换推导

    透视投影是3D固定流水线的重要组成部分,是将相机空间中的点从视锥体(frustum)变换到规则观察体(Canonical View Volume)中,待裁剪完毕后进行透视除法的行为.在算法中它是通过透 ...

  4. Shadow Mapping 的原理与实践(一)

    早在上世纪七十年代末,Williams在他的“Casting Curved Shadows on Curved Surface”一文中提出了名为Shadow Map的阴影生成技术.之后,他人在此基础上 ...

  5. Shadow Mapping 的原理与实践 【转】

    早在上世纪七十年代末,Williams在他的“Casting Curved Shadows on Curved Surface”一文中提出了名为Shadow Map的阴影生成技术.之后,他人在此基础上 ...

  6. three.js一个简单demo学些和讲解

    叉口裁剪球体案例 二话不说先上效果图: 全部代码带注释 <!DOCTYPE html> <html lang="en"> <head> < ...

  7. three.js学习笔记--基础知识

    基础知识 从去年开始就在计划中的three.js终于开始了 历史介绍 (摘自ijunfan1994的转载,感谢作者) OpenGL大概许多人都有所耳闻,它是最常用的跨平台图形库. WebGL是基于Op ...

  8. Unity Occlusion Culling 遮挡剔除研究

    本文章由cartzhang编写,转载请注明出处. 所有权利保留. 文章链接:http://blog.csdn.net/cartzhang/article/details/52684127 作者:car ...

  9. 【转】《Unity Shader入门精要》冯乐乐著 书中彩图

    为方便个人手机学习时候查阅,从网上转来这些彩图. 如属过当行为,联系本人删除. 勘错表 http://candycat1992.github.io/unity_shaders_book/unity_s ...

随机推荐

  1. CentOS 6.5 yum安装mysql5.6或其他版本【默认yum只能安装mysql 5.1】 by jason

    by jason [备份配置文件] CentOS 6.5 默认yum只能安装mysql 5.1 安装前要检查机器原来是否安装过mysql,如有安装需要先进行数据备份.清理. [root@snails ...

  2. GBDT 迭代决策树

    GBDT(Gradient Boosting Decision Tree) 又叫 MART(Multiple Additive Regression Tree),是一种迭代的决策树算法,该算法由多棵决 ...

  3. plsql 安装后database下拉没有东西(转)

    转载自:http://www.cnblogs.com/yaobolove/p/5682982.html 今天来说一下问题,就是装了plsql竟然在database这一栏没有东西,我也是纠结了很久,感觉 ...

  4. IBM ILOG JViews Charts 产品及功能介绍

    摘抄连接:http://www.ibm.com/developerworks/cn/websphere/library/techarticles/1004_lidb_ilogjchart/ IBM I ...

  5. css部分样式资料

    1. css字体 Lato,"Helvetica Neue","Segoe UI",Helvetica,Arial,sans-serif

  6. Linxu

    http://www.92csz.com/study/linux/ MySql 乱码  修改 /etc/my.cnf文件 character-set-server=utf8 , 表名不区分大小写:lo ...

  7. Java编程的逻辑 (30) - 剖析StringBuilder

    ​本系列文章经补充和完善,已修订整理成书<Java编程的逻辑>,由机械工业出版社华章分社出版,于2018年1月上市热销,读者好评如潮!各大网店和书店有售,欢迎购买,京东自营链接:http: ...

  8. 【jquery】基于 jquery 的翻牌效果 flip

    最近做了个类似于塔罗牌翻牌的效果,分享给大家. <!doctype html> <html lang="en"> <head> <meta ...

  9. Spring Cloud / Spring Boot There was an unexpected error (type=Unauthorized, status=401). Full authentication is required to access this resource.

    访问EndPoint时会出现没有权限   There was an unexpected error (type=Unauthorized, status=401). Full authenticat ...

  10. 关于网页游戏断线重连的思路和demo求助

    http://bbs.9ria.com/thread-146997-1-1.html —————————————————————————————————————————————————— 1:俺有什么 ...