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

背景

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

视锥体

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

空间平面方程可表示为:

    Ax+By+Cz=0

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

若 Ax1+By1+Cz1 = 0,则点在平面上;
若 Ax1+By1+Cz1 < 0,则点在平面的一侧;
若 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' 就在这个盒子里。

   -w' < x' < w'
-w' < y' < w'
-w' < z' < w'

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

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

    -w < x'

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

    −(v • row4 ) < (v • row1 )

    0 < (v • row4 ) + (v • row1 )

    0 < v • (row4 + row1 )

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

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

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

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

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

    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实现

float g_frustumPlanes[][];

void calculateFrustumPlanes( void )
{
float p[]; // projection matrix
float mv[]; // model-view matrix
float mvp[]; // model-view-projection matrix
float t; glGetFloatv( GL_PROJECTION_MATRIX, p );
glGetFloatv( GL_MODELVIEW_MATRIX, mv ); //
// Concatenate the projection matrix and the model-view matrix to produce
// a combined model-view-projection matrix.
// mvp[ ] = mv[ ] * p[ ] + mv[ ] * p[ ] + mv[ ] * p[ ] + mv[ ] * p[];
mvp[ ] = mv[ ] * p[ ] + mv[ ] * p[ ] + mv[ ] * p[ ] + mv[ ] * p[];
mvp[ ] = mv[ ] * p[ ] + mv[ ] * p[ ] + mv[ ] * p[] + mv[ ] * p[];
mvp[ ] = mv[ ] * p[ ] + mv[ ] * p[ ] + mv[ ] * p[] + mv[ ] * p[]; mvp[ ] = mv[ ] * p[ ] + mv[ ] * p[ ] + mv[ ] * p[ ] + mv[ ] * p[];
mvp[ ] = mv[ ] * p[ ] + mv[ ] * p[ ] + mv[ ] * p[ ] + mv[ ] * p[];
mvp[ ] = mv[ ] * p[ ] + mv[ ] * p[ ] + mv[ ] * p[] + mv[ ] * p[];
mvp[ ] = mv[ ] * p[ ] + mv[ ] * p[ ] + mv[ ] * p[] + mv[ ] * p[]; mvp[ ] = mv[ ] * p[ ] + mv[ ] * p[ ] + mv[] * p[ ] + mv[] * p[];
mvp[ ] = mv[ ] * p[ ] + mv[ ] * p[ ] + mv[] * p[ ] + mv[] * p[];
mvp[] = mv[ ] * p[ ] + mv[ ] * p[ ] + mv[] * p[] + mv[] * p[];
mvp[] = mv[ ] * p[ ] + mv[ ] * p[ ] + mv[] * p[] + mv[] * p[]; mvp[] = mv[] * p[ ] + mv[] * p[ ] + mv[] * p[ ] + mv[] * p[];
mvp[] = mv[] * p[ ] + mv[] * p[ ] + mv[] * p[ ] + mv[] * p[];
mvp[] = mv[] * p[ ] + mv[] * p[ ] + mv[] * p[] + mv[] * p[];
mvp[] = mv[] * p[ ] + mv[] * p[ ] + mv[] * p[] + mv[] * p[]; //
// Extract the frustum's right clipping plane and normalize it.
// g_frustumPlanes[][] = mvp[ ] - mvp[ ];
g_frustumPlanes[][] = mvp[ ] - mvp[ ];
g_frustumPlanes[][] = mvp[] - mvp[ ];
g_frustumPlanes[][] = mvp[] - mvp[]; t = (float) sqrt( g_frustumPlanes[][] * g_frustumPlanes[][] +
g_frustumPlanes[][] * g_frustumPlanes[][] +
g_frustumPlanes[][] * g_frustumPlanes[][] ); g_frustumPlanes[][] /= t;
g_frustumPlanes[][] /= t;
g_frustumPlanes[][] /= t;
g_frustumPlanes[][] /= t; //
// Extract the frustum's left clipping plane and normalize it.
// g_frustumPlanes[][] = mvp[ ] + mvp[ ];
g_frustumPlanes[][] = mvp[ ] + mvp[ ];
g_frustumPlanes[][] = mvp[] + mvp[ ];
g_frustumPlanes[][] = mvp[] + mvp[]; t = (float) sqrt( g_frustumPlanes[][] * g_frustumPlanes[][] +
g_frustumPlanes[][] * g_frustumPlanes[][] +
g_frustumPlanes[][] * g_frustumPlanes[][] ); g_frustumPlanes[][] /= t;
g_frustumPlanes[][] /= t;
g_frustumPlanes[][] /= t;
g_frustumPlanes[][] /= t; //
// Extract the frustum's bottom clipping plane and normalize it.
// g_frustumPlanes[][] = mvp[ ] + mvp[ ];
g_frustumPlanes[][] = mvp[ ] + mvp[ ];
g_frustumPlanes[][] = mvp[] + mvp[ ];
g_frustumPlanes[][] = mvp[] + mvp[]; t = (float) sqrt( g_frustumPlanes[][] * g_frustumPlanes[][] +
g_frustumPlanes[][] * g_frustumPlanes[][] +
g_frustumPlanes[][] * g_frustumPlanes[][] ); g_frustumPlanes[][] /= t;
g_frustumPlanes[][] /= t;
g_frustumPlanes[][] /= t;
g_frustumPlanes[][] /= t; //
// Extract the frustum's top clipping plane and normalize it.
// g_frustumPlanes[][] = mvp[ ] - mvp[ ];
g_frustumPlanes[][] = mvp[ ] - mvp[ ];
g_frustumPlanes[][] = mvp[] - mvp[ ];
g_frustumPlanes[][] = mvp[] - mvp[]; t = (float) sqrt( g_frustumPlanes[][] * g_frustumPlanes[][] +
g_frustumPlanes[][] * g_frustumPlanes[][] +
g_frustumPlanes[][] * g_frustumPlanes[][] ); g_frustumPlanes[][] /= t;
g_frustumPlanes[][] /= t;
g_frustumPlanes[][] /= t;
g_frustumPlanes[][] /= t; //
// Extract the frustum's far clipping plane and normalize it.
// g_frustumPlanes[][] = mvp[ ] - mvp[ ];
g_frustumPlanes[][] = mvp[ ] - mvp[ ];
g_frustumPlanes[][] = mvp[] - mvp[];
g_frustumPlanes[][] = mvp[] - mvp[]; t = (float) sqrt( g_frustumPlanes[][] * g_frustumPlanes[][] +
g_frustumPlanes[][] * g_frustumPlanes[][] +
g_frustumPlanes[][] * g_frustumPlanes[][] ); g_frustumPlanes[][] /= t;
g_frustumPlanes[][] /= t;
g_frustumPlanes[][] /= t;
g_frustumPlanes[][] /= t; //
// Extract the frustum's near clipping plane and normalize it.
// g_frustumPlanes[][] = mvp[ ] + mvp[ ];
g_frustumPlanes[][] = mvp[ ] + mvp[ ];
g_frustumPlanes[][] = mvp[] + mvp[];
g_frustumPlanes[][] = mvp[] + mvp[]; t = (float) sqrt( g_frustumPlanes[][] * g_frustumPlanes[][] +
g_frustumPlanes[][] * g_frustumPlanes[][] +
g_frustumPlanes[][] * g_frustumPlanes[][] ); g_frustumPlanes[][] /= t;
g_frustumPlanes[][] /= t;
g_frustumPlanes[][] /= t;
g_frustumPlanes[][] /= t; } bool isBoundingSphereInFrustum( float x, float y, float z)
{
for( int i = ; i < ; ++i )
{
if( g_frustumPlanes[i][] * x +
g_frustumPlanes[i][] * y +
g_frustumPlanes[i][] * z +
g_frustumPlanes[i][] <= )
return false;
} return true;
}

视锥体(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. Redis 相关操作

    1.安装 下载地址:http://www.redis.cn/ 在使用Redis时,开始就遇到了问题,客户端打不开,原因是需要先开启服务端,这需要先配置—— 1.下载好redis安装包,解压安装之后,复 ...

  2. struts学习笔记

    ------struts in action 读书笔记 1. ActionServlet:Struts 的ActionServlet控制导航流.当ActionServlet从容器接到一个请求,它使用U ...

  3. Json转list,二层解析转换

    一层结构的数据: { "code": "0", "results": { "boyTotal": 0, "cl ...

  4. C语言 · 校门外的树

    算法提高 校门外的树   时间限制:1.0s   内存限制:256.0MB      问题描述 某校大门外长度为L的马路上有一排树,每两棵相邻的树之间的间隔都是1米.我们可以把马路看成一个数轴,马路的 ...

  5. C语言 · 字符删除

    算法训练 字符删除   时间限制:1.0s   内存限制:512.0MB      问题描述 编写一个程序,先输入一个字符串str(长度不超过20),再输入单独的一个字符ch,然后程序会把字符串str ...

  6. C语言 · 确定元音字母位置

    算法训练 确定元音字母位置   时间限制:1.0s   内存限制:512.0MB      输入一个字符串,编写程序输出该字符串中元音字母的首次出现位置,如果没有元音字母输出0.英语元音字母只有‘a’ ...

  7. Putty远程SSH免密码方式链接Linxu

    1.首先确保Linxu服务器安装了ssh服务,可以输入ssh命令测试. ssh [-1246AaCfgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spe ...

  8. CPP_template

    泛型编程是独立于任何特定类型的方式编写代码.模板是泛型编程的基础,模板使程序员能够快速建立具有类型安全的类库集合和函数集合,它的实现,方便了大规模的软件开发. 模板提供通用类型和通用函数,定义中包含t ...

  9. python多进程并发

    由于Python下调用Linux的Shell命令都需要等待返回,所以常常我们设置的多线程都达不到效果,因此在调用shell命令不需要返回时,使用threading模块并不是最好的方法.   http: ...

  10. TIDB介绍 新数据库趋势

    TIDB是什么? TIDB 受谷歌Spanner和F1的论文启发的new sql数据库,这类数据库不仅具有NoSQL对海量数据的存储管理能力,还保持了传统数据库支持ACID和SQL等特性,同类数据库还 ...