From : http://cesiumjs.org/2013/04/25/Horizon-culling/
在虚拟地球(如, Cesium)的开发过程中, 我们需要能够快速确定场景中的对象, 如地形瓦片(terrain tiles), 卫星(satellites), 建筑物(buildings), 交通工具(vehicles)等, 何时是不可见的。因为这些对象不可见, 所以也就没有必要被渲染。我们当然需要进行视域(view-frustum)的剔除。但是, 地平线剔除(horizon culling)是另一个重要的剔除类型。
在上图中, 绿色的点是观察者(viewer)是可见的。红色的点是不可见的,因为它们位与视域(两条白色的粗线中间的那部分区域)之外。蓝色的点虽然处于视域的范围内, 但它们依然是不可见的, 因为它们被地球遮挡住了。换句话说, 它们处于地平线之下。地平线剔除是一个非常浅显的一种思路: 你不需要去渲染那些相对当前观察者的位置处于地平线之下的对象。尽管听起来很简单易懂,细节的处理非常棘手, 尤其是这个过程要求得到非常快的处理。Cesium 需要对每个渲染帧做成千上万次的这种测试以确定地形瓦片的可见性。但, 这种测试是非常重要的。在上图的配置中,覆盖整个地球的地形瓦片都处于视域中。然而, 有一半以上的地形瓦片处于地平线以下,不需要被渲染。
几年前, Deron Ohlarik 写了两篇关于地平线剔除的非常优秀的文章。自此以来, 我们一直致力于扩展他的技术,我将在这里分享我们的成果。虽然它只适用于静态数据如地形瓦片,但我们发现这非常有用, 因为它比先前的技术处理地更快、更精确。对水平线剔除的精度的改善源于地球的椭球模型而非近似一个球面。
在此之前,我应该提到对这种技术的信心完全是因为我的同事——Frank Stoner. 我所做的贡献只是在 Cesium 中实现了它,以及写在这里,而他做了最艰难的工作是得到这种技术。
Horizon culling a point against a sphere
正如Ohlarik所描述的, 为了进行地平线剔除, 我们可以为一个静态对象(如一个地形瓦片),即便仅是一个点,计算一个边界范围。如果这个点处于地平线下以下,那么我们就能确信这个瓦片也处于地平线以下。我们的新技术限制于参照一个椭球来剔除单个点,因此我们开始假定这个被遮挡的点已经被计算过了。关于具体做法,请查看后续博客。
我承诺,我们将实现参照一个一般椭球的水平剔除。我也会兑现这个诺言,但是让我们先从一个简单的单位球出发来介绍地平线剔除。然后, 我会说明如何将这个单位球推广到一个任意的椭球。考虑下面这幅图:
在上面这幅图中, 蓝色的圆就是我们的单位球。从相机位置引出的两条单位球的切线表示了视域。图中黑色的竖线表示了所有的地平线上的点。在我们的单位球面上,地平线上的点处在一个平面上,并形成了一个圆。从相机位置到所有地平线上的点的向量构成了一个无限延伸的圆锥体。
灰色阴影的部分球体以及它周围的空间表示了位于地平线以下的区域。阴影区域中的任何点从相机位置上来看,都是不可见的。直观地说,如果一个点处于正切向量构成的无限圆锥体内部并且位于地平线上所有点构成的平面的后面,那么这个点位于地平线之下。
The Plane test
首先, 让我们进行一项廉价的测试来确定一个点位于一个平面的那一侧。考虑下面的这幅图:
我们知道向量VC 和向量VT: 它们分别表示观察者到目标点和椭球中心的向量。我们也知道HC是个单位向量, 因为我们现在处理的是一个单位球。根据毕氏定理(Pythagorean theorem):
接下来,我们注意到三角形VCH和HCP是相似三角形。它们共享一个点C的上的角,另外,它们都有一个90度的直角。因此:
因此, 观察者到地平面的距离是:
向量VT在向量VC上的投影小于||VP||,目标点位于平面前侧。换句话说, 目标点位于地平面后侧当:
我们可以简化这个公式,通过两边同时乘以||VC||2, 结果变成:
为了确定目标点是否位于地平面后侧,取观察者到目标点的向量与观察者到椭球中心的向量的点积。如果得到的值比观察者到椭球中心的向量的大小平方值减1,那么目标点就位于该平面后侧。不需要进行平方根或三角函数运算。
The Cone Test