譯者注:本文翻譯自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%的瓦片,不然咱們會在普通場景中繪製。使人高興的是,新測試對每一個圖塊的執行速度也更快!
到目前爲止,咱們繞過的一個細節是咱們如何從咱們的地形圖塊和其餘靜態幾何體生成「被遮擋物」測試點。目前,咱們正在根據(錯誤但保守的)假設計算每一個瓦片的被遮擋點,即便用由橢圓體的最小半徑造成的球體來執行遮擋。經過對被遮擋點使用更準確的計算,咱們應該可以剔除更多的圖塊。
更新:這在後續文章中有更詳細的介紹。
然而,雖然橢球是用於地平線剔除的方便且至關準確的表面,但咱們必須始終牢記,真實地形一般位於橢球下方。若是咱們改進被遮擋點的計算,咱們必須注意,相對於橢球更準確的地平線剔除最終不會剔除相對於真實地形實際上仍然可見的瓦片。在渲染水下地形時,這尤爲可能成爲一個問題。