获取View Frustum的6个平面

作者:i_dovelemon

来源:CSDN

日期:2014 / 9 / 30

主题:View Frustum, Plane, View Matrix, Perspective Projection Matrix

引言

在3D图形学中,一种优化的手法,叫做物体剔除(Object Culling)。这种技术的提出是基于这样的策略:我们不希望将不存在View Frustum里面的物体送往流水线中进行处理。虽然,你可能会说,现在的图形卡都已近支持了三角形剔除的技术。但是,不要忘了流水线的步骤。我们是先进行Vertex Processing,然后才进行Geometry Processing。而图形卡对三角形的剔除是在Geometry
Processing这个阶段进行的。也就是说,即使能够剔除它们,我们依然要在Vertex Processing中对这些将来需要丢弃的顶点进行Vertex Processing处理。而我们知道,Vertex Processing这个阶段往往会进行坐标变换,光照计算等等费时的操作。所以,如果能够在Vertex Processing之前,也就是在Application阶段,在我们的CPU中将这些根本不需要处理的物体剔除的话,会大大的提高程序的效率。而想要将物体剔除,我们就需要知道这个物体是否在View Frustum中。而为了判断这个条件,我们就需要确定构成View
Frustum的6个平面的方程。所以,本篇文章,就向大家讲述,如何获取View Frustum的6个平面方程。

推理过程

想要获取View Frustum的6个面的方程,那么我们就需要知道,我们是怎么定义View Frustum的。在我的博客栏目中,有一篇文章基本3D变换。在这篇文章中,我们知道,我们使用NearZ, FarZ, Fov和Aspect这几个值来定义了View
Frustum。而这些值都保存在Projection Matrix中。所以,突破口就在这个Projection Matrix里面。

我们来回顾下上面的文章。基本3D变换这篇文章中讲述了基本的3D变换之World Transform, View Transform和Projection Transform。并且费了很大的篇幅证明了Projection Matrix的作用以及它的推导过程。从中我们知道,Projection
Matrix的作用是将View Frustum中的顶点,变换到一个Cubic空间中去。这个空间的X的范围为[-1,1],Y轴的范围为[-1,1],Z轴的范围为[0,1]。那么,也就是说,原来View Frustum的6个平面,在经过了变换之后,变成了Cubic的6个面。那么,我们能否通过这6个面,来获取还原原来的View Frustum的6个面了。答案是肯定的。但是我们并不是直接获取到6个平面的方程,而是使用一种等价的方式推导出这6个面的。下面我们来看下。

我们知道,在世界坐标空间View Frustum中的一个点S,经过了M = ViewMatrix * ProjMatrix变换之后,就变换到了Cubic的空间中去了。所以,我们可以得到如下的结论:

T = S * M   (1)

变换后的T是一个齐次坐标,它的w分量不再是1,我们来看下T.w到底等于什么。

在世界坐标空间中,顶点的w值都为1。所以与View Matrix相乘之后,它的w变成了 :

w‘ = [x,y,z,w] * [ViewM.03, ViewM.13, ViewM.23, ViewM.33] = [x,y,z,w] * [0, 0, 0, 1] = w = 1 (2)

再与Proj Matrix相乘之后,w‘变成了:

w‘‘ = [x‘,y‘,z‘,w‘] * [ProjM.03, ProjM.13, ProjM.23, ProjM.33] = [x, y, z , w‘] * [ 0, 0, 1, 0] = z‘ (3)

也就是说,顶点在经过了View Transform 和 Projection Transform之后,它的w分量变成了顶点在相机空间中的z值。

我们将T化成U,其中U.w = 1。而现在的U就处在Cubic空间中。我们知道Cubic空间的X范围为[-1,1],那么就是说:

-1 <= U.x <= T.x / T.w (4)

通过公式(3),我们知道T.w = z‘,即等于在相机空间中的z值。而View Frustum中的顶点在相机坐标空间中的z值都是大于0的。所以又可以得到如下的公式:

T.x + T.w >= 0 (5)

通过公式(1),我们可以知道如下:

T.x = S * Mcol0 , T.w = S * Mcol3 (6)

将公式(6)带入公式(5),得到如下:

S*Mcol0 + S*Mcol3 >= 0  ==>  S * (Mcol0 + Mcol3) >= 0 (7)

由于Mcol0和Mcol3都是一个向量,他们的和也可以用一个向量N来表示,所以就变成了

S * N >= 0  ==> S.x * N.x + S.y * N.y + S.z * N.z + S.w * N.w >= 0 ==> S.x*N.x + S.y*N.y + S.z*N.z + N.w >= 0 (8)

我们来回想下,在DirectX中,平面是如何保存的。在DirectX中,D3DXPLANE中有两个属性来唯一确定。它们分别是平面的法向向量N‘,以及原点到这个平面的有向距离d来表示。如果一个点S’在平面的正半空间中,那么我们将这个点带入到DirectX的平面方程中:

S‘ * N‘ + d > 0 ==> S‘.x*N‘.x + S‘.y*N‘.y + S‘.z*N‘.z + d >= 0 (9)

读者自己比对下公式(8)和公式(9),就会发现,它们是惊人的相似。只不过这里的d,变成了N.w而已。所以,我们完全可以将公式(8)中的N和N.w抽取出来,作为D3DXPLNE中的N和d,来构成一个平面。而这个平面就是View Frustum的Left平面。在上面,我们已经假设了U.x>=-1,也就是说,这里的情况下,得到的Left平面,它的法向向量是指向View Frustum里面的。如果想要指向外面只要将公式(8)中的N反向即可。

同样的方法,我们可以推导出其他几个面的方程。

总结

通过上面的推导,我们就可以得到如下的结论:

Left平面: 0 =( Mcol3 + Mcol0) * [x,y,z,w]

Right平面: 0 = (Mcol3 - Mcol0) * [x,y,z,w]

NearZ平面: 0 = Mcol2 * [x,y,z,w]

FarZ平面: 0 = (Mcol3 - Mcol2) * [x,y,z,w]

Top平面: 0 = (Mcol3 - Mcol1) * [x,y,z,w]

Bottom平面:  0 = (Mcol3 + Mcol1) * [x,y,z,w]

不过注意,上面的平面都是没有进行归一化的平面。使用的时候,最好使用D3DXNormalizePlane进行归一化操作。

好了,今天就到这里。下一篇文章,会向大家讲述,如何进行检测,判断物体是否在View Frustum外部。

时间: 2024-11-12 09:06:06

获取View Frustum的6个平面的相关文章

得到View Frustum的6飞机

笔者:i_dovelemon 资源:CSDN 日期:2014 / 9 / 30 主题:View Frustum, Plane, View Matrix, Perspective Projection Matrix 引言 在3D图形学中,一种优化的手法,叫做物体剔除(Object Culling). 这种技术的提出是基于这种策略:我们不希望将不存在View Frustum里面的物体送往流水线中进行处理. 尽管,你可能会说,如今的图形卡都已近支持了三角形剔除的技术.可是,不要忘了流水线的步骤. 我们

View Frustum Culling

作者:i_dovelemon 来源:CSDN 日期:2014 / 10 / 28 主题:View Frustum, Culling 引言 在前面的一篇文章获取View Frustum的6个面中讲述了如何根据View-Proj矩阵来获取View Frustum在世界坐标系中的6个平面.研究过场景管理的同学就会知道,在将图元数据传入到流水线之前,我们需要对数据进行组织.而场景管理通常就是进行这样的工作,通过场景管理,我们剔除(Culling)那些不在View Frustum中的物体,也就是在显示器中

在渲染前获取 View 的宽高

在渲染前获取 View 的宽高 这是一个比较有意义的问题,或者说有难度的问题,问题的背景为:有时候我们需要在view渲染前去获取其宽高,典型的情形是,我们想在onCreate.onStart.onResume中去获取view的宽高.如果大家尝试过,会发现,这个时候view还没有measure好,宽高都为0,那到底该怎么做才能正确获取其宽高呢,下面给出三种方法(还有其他方法, 比如监听器回调等): Activity/View#onWindowFocusChanged :这个方法表明,view已经初

Screen position out of view frustum

Screen position out of view frustum (screen pos 512.000000, 0.000000, 100.000000) (Camera rect 0 0 512 512) UnityEngine.Camera:Render() MirrorReflection2:OnWillRenderObject() (at Assets/UnityChanStage/Visualizer/MirrorReflection2.cs:118) 在用unity3d调优时

解决在onCreate()过程中获取View的width和Height为0的4中方法

很经常当我们动态创建某些View时,需要通过获取他们的width和height来确定别的view的布局,但是在onCreate()获取view的width和height会得到0.view.getWidth()和view.getHeight()为0的根本原因是控件还没有完成绘制,你必须等待系统将绘制完View时,才能获得.这种情况当你需要使用动态布局(使用wrap_content或match_parent)就会出现.一般来讲在Activity.onCreate(...).onResume()方法中

Activity中获取view的高度和宽度为0的原因以及解决方案

在activity中可以调用View.getWidth.View.getHeight().View.getMeasuredWidth() .View.getgetMeasuredHeight()来获得某个view的宽度或高度,但是在onCreate().onStrart().onResume()方法中会返回0,这是应为当前activity所代表的界面还没显示出来没有添加到WindowPhone的DecorView上或要获取的view没有被添加到DecorView上或者该View的visibili

spring mvc DispatcherServlet详解之三---request通过ModelAndView中获取View实例的过程

整个spring mvc的架构如下图所示: 上篇文件讲解了DispatcherServlet第二步:通过request从Controller获取ModelAndView.现在来讲解第三步:request 从ModelAndView中获取view对象. 获取view对象一般是通过viewResolver来解析view name来完成的.若ModelAndView中view 不存在或者ModelAndView本身为null则填充默认值.代码如下: ModelAndView中view 不存在或者Mod

解决获取View的width和Height为0的4种方法

很经常当我们动态创建某些View时,需要通过获取他们的width和height来确定别的view的布局,但是在onCreate()获取view的width和height会得到0.view.getWidth()和view.getHeight()为0的根本原因是控件还没有完成绘制,你必须等待系统将绘制完View时,才能获得.这种情况当你需要使用动态布局(使用wrap_content或match_parent)就会出现.一般来讲在Activity.onCreate(...).onResume()方法中

BaseAdapter获取View之三重境界

在BaseAdapter获取View之前,BaseAdapter需要与数据源相关联. 可以使用构造方法: private List<ItemBean> baseListItems; private LayoutInflater mInflate; //布局装载器 public MyBaseAdapter(Context context,List<ItemBean> listItems){ baseListItems = listItems; //将数据源与数据适配器关联 mInfl