译者注:本文翻译自Cesium官方博文《Horizon Culling》,by KEVIN RING。javascript
在开发像Cesium这样的虚拟数字地球时,咱们须要可以快速肯定场景中的对象(例如地形图块,卫星,建筑物,车辆等)什么时候不可见,所以不须要渲染。固然,咱们进行视锥体裁剪。可是,另外一种重要的剔除类型是地平线剔除。java
在上图中,观看者能够看到绿点。 红点不可见,由于它们在视锥面以外,用粗白线表示。 蓝点位于视锥中,但因为地球遮挡住,所以观看者看不到。 换句话说,它在地平线之下。 “地平线剔除”是一个简单的想法,您无需渲染从当前查看器位置观察到的位于地平线如下的对象。 听起来很简单,但细节变得棘手,特别是由于它须要很是快。 Cesium会对每一个渲染帧进行数百次此测试,以测试地形图块的可见性。 不过,这是一项重要的测试。 在上图中的配置中,覆盖整个地球的地形图块位于视锥中。 可是,其中有一半以上不在地平线范围内,不须要渲染。并发
几年前,Deron Ohlarik写了两篇有关地平线剔除的出色文章。 此后,咱们对他的技术进行了扩展,我想在这里分享。 尽管它仅适用于地形图之类的静态数据,但咱们发现它很是有用,由于它比之前的技术更快,更准确。 精度的提升来自对地球的椭球模型的视界剔除,而不是球面近似。函数
我首先要提到,这项技术的功劳彻底归功于个人同事弗兰克·斯通纳(Frank Stoner)。 我所作的惟一贡献就是在他作了艰辛的工做后,在Cesium中实现了它,并在此处进行了编写。测试
如Ohlarik所述,出于水平剔除的目的,咱们能够为静态对象(例如地形图块)计算边界球,该边界球是如此紧密以致于它仅仅是一个点。 若是该点在地平线如下,那么咱们能够确保整个图块也在地平线如下。 咱们的新技术仅限于针对椭球体选出一个点,所以咱们假设此“遮挡点”已被计算出来。 有关如何完成此操做的详细信息,请参见后续博客文章。spa
我保证咱们会针对普通的椭球体实施视界剔除,而我会兑现这一诺言,但让咱们首先使用一个简单的单位球体进行视界剔除。 而后,我将证实咱们能够轻松地将其归纳为任意的椭球体。 考虑下图:翻译
在此图中,蓝色圆圈是咱们的单位球面。 从摄影机位置延伸并与球体相切的线表明地平线。黑色垂直线表明全部地平线点。在咱们的单位球面上,地平线点位于平面上并造成一个圆。从摄像机位置到全部地平线点的向量造成一个无限锥。设计
球体的部分及其周围的空间以灰色阴影表示表明地平线如下的区域。从摄像机位置看不到阴影区域中的任何点。直观地说,若是该点位于由切向量造成的无限锥内,则该点位于地平线下方,而且位于包含全部地平线点的平面以后。3d
首先,让咱们进行一项代价很小的测试,以肯定一个点在平面的哪一侧。 考虑下图:code
咱们知道向量\(\vec{VC}\)和\(\vec{VH}\)分别是相点到目标点和相点到椭球中心点的向量。同时,因为这里使用的是单位球,向量\(\vec{HC}\)是一个单位向量,根据勾股定理:
接下来,咱们注意到三角形△VCH和△HCP是类似三角形。他们共享一个位于C点的角而且都有一个直角,所以:
所以,从视点到平面的距离为:
若是\(\vec{VT}\)在\(\vec{VC}\)上的投影小于\(\lVert \vec{VP} \rVert\),那么目标点就在平面以前。换句话说,目标点在视平面以后的条件是:
两边同时乘以\(\lvert \vec{VC} \rvert\):
综上所述,要肯定目标点是否在视线平面以后,可使用视点到目标点的矢量,与视点到椭球体的中心的矢量的点积。 若是该值大于从观察者到椭球中心的向量的模的平方减一,则目标点在平面后面。不须要开平方或三角函数操做。
若是目标点在视平面前面,那么该目标点绝对不会被球体遮挡,此时工做就完成了。可是,若是它在视平面后方,可否被遮挡是不肯定的。若是目标点也在,视点与全部地平线点链接而造成的无限锥体内,则它被遮挡。若是它在那个圆锥体以外,那么它不会被遮挡。那么咱们如何经过圆锥测试点呢?
让咱们再次看一下图,此次是角度∠HVC标记为α,∠TVC标记为β:
能够看到,若是点T要在圆锥体内,那么:
对于\(0<=θ<=π\),有:
角α是直角三角形△VCH的一部分,因此咱们经过三角函数,重写不等式的右边:
根据点积的定义,有:
为了求平方根的操做,两边都进行平方:
经过对两边进行平方,针对对顶圆锥,咱们能有效地测试目标点,其中第二个锥体从观察者指向远离椭圆体。然而,这不会影响咱们的结果,由于观察者后面的目标点确定在地平线前面。视平面前面的任何点都不能被地平线剔除,所以不须要第二个锥体测试。
如今咱们站在哪里?\(\vec{VC}\)和\(\vec{VT}\)很容易从咱们已知的椭球中心、目标点和观察者位置计算出来。\(\vec{VH}\)不是那么明显。可是还记得在对视平面进行测试的部分吗?咱们发现:
这不只容易计算,并且咱们在肯定点在平面的哪一侧的过程当中已经这样作了。相似地,咱们已经计算了\(\vec{VT}\cdot\vec{VC}\)。
因此咱们最终的不等式,只须要一点更多的算术运算来评估,以下所示:
若是此不等式成立,则目标点在锥体内部。若是它也在地平线后面,则目标点被遮挡。
在咱们漂亮的小单位球世界中,这一切都很是优雅。 咱们如何将其推广到任意椭球体?咱们的单位球面方程为:
而椭球的方程为:
其中a,b和c分别是椭圆体沿x,y和z轴的半径。
给定一个以原点为中心的椭球、一个观察者位置和一个目标位置,咱们能够对全部坐标应用缩放变换,以建立一个等效的问题,其中椭球其实是一个单位球体。 执行缩放操做的矩阵以下所示:
咱们将此缩放坐标系称为椭球缩放空间,并发现它对于解决椭球上的各类问题颇有用。
能够在SIGGRAPH 2010上展现的海报GPU Ray Casting of Virtual Globes的第2节中找到对该主题的更严密的处理。
我认为把全部的数学都写出来很重要,但这一切都归结为一些简单的代码。每次相机位置改变时,咱们执行:
// Ellipsoid radii - WGS84 shown here var rX = 6378137.0; var rY = 6378137.0; var rZ = 6356752.3142451793; // Vector CV var cvX = cameraPosition.x / rX; var cvY = cameraPosition.y / rY; var cvZ = cameraPosition.z / rZ; var vhMagnitudeSquared = cvX * cvX + cvY * cvY + cvZ * cvZ - 1.0;
而后,对于咱们但愿测试遮挡剔除的每一个点:
// Target position, transformed to scaled space var tX = position.x / rX; var tY = position.y / rY; var tZ = position.z / rZ; // Vector VT var vtX = tX - cvX; var vtY = tY - cvY; var vtZ = tZ - cvZ; var vtMagnitudeSquared = vtX * vtX + vtY * vtY + vtZ * vtZ; // VT dot VC is the inverse of VT dot CV var vtDotVc = -(vtX * cvX + vtY * cvY + vtZ * cvZ); var isOccluded = vtDotVc > vhMagnitudeSquared && vtDotVc * vtDotVc / vtMagnitudeSquared > vhMagnitudeSquared;
在 Cesium 中,咱们预先计算缩放空间位置,而不是在每次测试以前进行,如上所示。
使用这种技术在Cesium中进行地形剔除,与咱们以前使用最小半径边界球剔除的技术相比,咱们能够避免绘制大约15%的瓦片,不然咱们会在普通场景中绘制。使人高兴的是,新测试对每一个图块的执行速度也更快!
到目前为止,咱们绕过的一个细节是咱们如何从咱们的地形图块和其余静态几何体生成“被遮挡物”测试点。目前,咱们正在根据(错误但保守的)假设计算每一个瓦片的被遮挡点,即便用由椭圆体的最小半径造成的球体来执行遮挡。经过对被遮挡点使用更准确的计算,咱们应该可以剔除更多的图块。
更新:这在后续文章中有更详细的介绍。
然而,虽然椭球是用于地平线剔除的方便且至关准确的表面,但咱们必须始终牢记,真实地形一般位于椭球下方。若是咱们改进被遮挡点的计算,咱们必须注意,相对于椭球更准确的地平线剔除最终不会剔除相对于真实地形实际上仍然可见的瓦片。在渲染水下地形时,这尤为可能成为一个问题。