原文地址: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. spring 强制采用cglib进行代理

    spring对AOP的支持 *如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP* 如果目标对象实现了接口,可以强制使用CGLIB实现AOP*如果目标对象没有实现了接口,必须采用CGL ...

  2. 2. 集成学习(Ensemble Learning)Bagging

    1. 集成学习(Ensemble Learning)原理 2. 集成学习(Ensemble Learning)Bagging 3. 集成学习(Ensemble Learning)随机森林(Random ...

  3. 别人不会给你说的---C语言中数组名和指针的区别 及 sizeof用法

    引自: http://blog.csdn.net/tianyue168/article/details/5781924 #i nclude <iostream.h> int  main( ...

  4. virtualbox ubuntu 虚拟画面卡顿问题

    要在虚拟机全局配置里面添加选项:

  5. ActiveMQ两种模式PTP和PUB/SUB<转>

    1.PTP模型 PTP(Point-to-Point)模型是基于队列(Queue)的,对于PTP消息模型而言,它的消息目的是一个消息队列(Queue),消息生产者每次发送消息总是把消息送入消息队列中, ...

  6. docker原理

    Docker原理11 Linux Namespace 11 AUFS文件系统17 重新理解Docker的各种命令18 Docker原理 Linux Namespace docker是一个容器引擎,容器 ...

  7. WebForm发送邮件

    using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Ne ...

  8. JavaScript(九):JavaScript中的内置对象

    一.对象的概念 对象是一种特殊的数据类型(object). 拥有一系列的属性和方法. 分为内置对象和自定义对象. 二.String对象 String对象用来支持对于字符串的处理. 1.属性 lengt ...

  9. Spring Cloud Config 自动刷新所有节点

    全局刷新 详细参考:<Sprin Cloud 与 Docker 微服务架构实战>p160-9.9.2节 1.使用Spring Cloud Config 客户端时,可以使用 /refresh ...

  10. PCL 可视化

    可视化(visualization)是利用计算机图形学和图像处理技术,将数据转换图像在屏幕上显示出来,并进行交互处理的的理论,方法和技术, pcl_visualization库建立了能够快速建立原型的 ...