获取View Frustum的6个平面

本文聚焦3D图形学中的物体剔除技术,指出在Vertex Processing前剔除物体可提高程序效率。而要剔除物体,需确定构成View Frustum的6个平面方程。文章详细讲述了获取这些平面方程的方法,还给出了相关代码示例及基于裁剪空间特性的推导过程。

在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个平面方程。

 

void Frustum::createPlane(const Camera* camera)

{

    const Mat4& mat = camera->getViewProjectionMatrix();  //获得视图投影矩阵

    //ref http://www.lighthouse3d.com/tutorials/view-frustum-culling/clip-space-approach-extracting-the-planes/

    //extract frustum plane

    //  (X x a11 + Y x a12 + Z x a13 + a14) + (X x a41 + Y x a42 + Z x a43 + a44) > 0

    //  X(a11 + a41) + Y(a12 + a42) + Z(a13 + a43) + a14 + a44 > 0

    // 负数大概是因为 p.n = d p就是(x,y,z),n就是(a11 + a41,a12 + a42,a13 + a43) d如果不变的话,n就应该取负数

    _plane[0].initPlane(-Vec3(mat.m[3] + mat.m[0], mat.m[7] + mat.m[4], mat.m[11] + mat.m[8]), (mat.m[15] + mat.m[12]));//left

    _plane[1].initPlane(-Vec3(mat.m[3] - mat.m[0], mat.m[7] - mat.m[4], mat.m[11] - mat.m[8]), (mat.m[15] - mat.m[12]));//right

    _plane[2].initPlane(-Vec3(mat.m[3] + mat.m[1], mat.m[7] + mat.m[5], mat.m[11] + mat.m[9]), (mat.m[15] + mat.m[13]));//bottom

    _plane[3].initPlane(-Vec3(mat.m[3] - mat.m[1], mat.m[7] - mat.m[5], mat.m[11] - mat.m[9]), (mat.m[15] - mat.m[13]));//top

    _plane[4].initPlane(-Vec3(mat.m[3] + mat.m[2], mat.m[7] + mat.m[6], mat.m[11] + mat.m[10]), (mat.m[15] + mat.m[14]));//near

    _plane[5].initPlane(-Vec3(mat.m[3] - mat.m[2], mat.m[7] - mat.m[6], mat.m[11] - mat.m[10]), (mat.m[15] - mat.m[14]));//far

}

 

 

 

 

 

 

 

Clip Space Approach – Extracting the Planes

  

In here another approach to extract the view frustum planes is presented based on the properties of clip space.

Consider a point p =(x,y,z,1) on the 3D world. Consider also a modelview matrix Mand a projection matrix P. The point p is transformed by matrices M and P as point pc =(xc,yc,zc,wc) in clip space using:

The point pc is in homogeneous coordinates, and when normalised becomes pcn:

In normalised clip space the view frustum is an axis aligned box centered in the origin, and bounded by the following planes:

  • Left Plane: x’ = -1
  • Right Plane: x’ = 1
  • Top Plane: y’ = 1
  • Bottom Plane: y’ = -1
  • Near Plane: z’ = -1
  • Far Plane: z’ = 1

This implies that the point pcn =(x’,y’,z’) is inside the view frustum if:

Then the point pc, in non-normalized coordinates, must obbey the following conditions in order to be inside the view frustum:

Based on this information it is possible to extract the six planes, in world coordinates, that bound the view frustum. The point pc is on the “right” side of the left plane if

Consider p and A=MP as described below

Then xc and wc can be defined as a function of p = (x,y,z,w), and A.

Therefore the following inequation must be true if p is on the right side of the left plane.

A little algebraic manipulation gives

So the left plane (Ax+By+Cz+D=0) is defined as:

where col1 and col4 are the first and forth columns of matrix A, respectively.

If the only objective is to find out if a point is inside or outside of the frustum then the plane as defined is ok. However when testing spheres, which require computing the distance from the center of the sphere to the plane, it is advisable to normalize the plane.

The right plane can be obtained in a similar manner:

The following coefficients are obtained for the right plane:

The remaining planes are obtained as follows:

Bottom Plane

Top Plane

Near Plane

Far Plane

在 Cesium 中,获取当前相机视角下的可视区域经纬度范围是一项非常实用的功能,尤其适用于分析可见范围内的地理信息或设置特定的地图边界条件。 以下是关于如何实现这一目标的技术细节与步骤说明: --- ### 实现思路: 为了得到当前视口范围内所有点覆盖的最大矩形界限(Bounding Rectangle),我们可以通过以下方式完成任务: 1. 将屏幕四角(左上、右上、左下、右下四个像素点)分别逆向映射回地球表面对应的大地测量坐标系中的位置; 2. 找出这些坐标当中最小和最大的经度以及纬度值组合起来形成最终的包围盒定义; 这里需要用到两个关键函数分别是 `camera.positionCartographic` 和前面提到过的 `scene.globe.cartesianToCartographic()` 或者更简单的版本如 `scene.pickEllipsoid()` 函数配合工作。 #### 示例代码: 下面提供了一段 JavaScript 例子展示怎样取得这样的一个边界框数值对儿出来: ```javascript function getViewBounds(scene) { var frustum = scene.camera.frustum; if (!(frustum instanceof Cesium.PerspectiveFrustum)) { return; } // Define the corners of the viewport in window coordinates. var width = scene.canvas.clientWidth; var height = scene.canvas.clientHeight; var positions = [ new Cesium.Cartesian2(0, 0), // Top-left corner. new Cesium.Cartesian2(width - 1, 0), // Top-right corner. new Cesium.Cartesian2(0, height - 1), // Bottom-left corner. new Cesium.Cartesian2(width - 1, height - 1)// Bottom-right corner. ]; var cartographics = []; for (var i = 0; i < positions.length; ++i) { var position = Cesium.SceneTransforms.wgs84PositionToViewportCoordinates( scene, Cesium.Cartesian3.fromRadians(-Math.PI / 6.0, Math.PI / 6.0) ); var ray = scene.camera.getPickRay(position); var earthPosition = scene.globe.pick(ray, scene); if (!Cesium.defined(earthPosition)) continue ; let c = Cesium.Cartesian3.toCartographic(earthPosition); cartographics.push(c); } if(cartographics.length === 0){ console.warn("无法确定视野内有效坐标!"); return null ; } function calcMinMax(array,index){ return array.reduce((p,c)=>{return [Math.min(p[0],c[index]),Math.max(p[1],c[index])]},[Number.MAX_VALUE,-Number.MAX_VALUE]); } const lngRange =calcMinMax(cartographics.map(it=>it.longitude ),undefined); const latRange=calcMinMax(cartographics.map(it=>it.latitude ),undefined); return {west:lngRange[0]*RAD2DEG,east:lngRange[1]*RAD2DEG,north:latRange[1]*RAD2DEG,south:latRange[0]*RAD2DEG}; } // Usage Example: let bounds=getViewBounds(viewer.scene); if(bounds!=null){ console.info('Current View Bounds:',bounds); } ``` 上述脚本首先构建起了整个窗口尺寸基础上面分布的所有顶点集合列表。然后逐一尝试沿着每条光线寻找碰撞到了地面的确切三维空间坐标表达式,并将其转化为平面图表征形式存储下来直至结束为止最后才统一求解出全局最外围极限情况而已啦! ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值