View Frustum Culling Tutorial

1 簡介

爲了從不同的角度可視化一個場景(Scene),虛擬相機(virtual camera)就經常派上用場了。虛擬相機的設置(OpenGL中常通過gluPerspectivegluLookAt函數設置)決定了從屏幕中可以看到什麼。

View平截頭體(frustum)包羅了當前屏幕上一切潛在的可見實體(之所以說潛在的可見實體,是因爲有些實體可能被遮擋住了)。它通過相機的設置來定義,當使用透視投影(perspective projection)時,平截頭體就像一個被截掉塔尖的金字塔。

金字塔的頂點就是相機的位置,塔底就是遠平面(far plane)。金字塔在近平面(near plane)位置截斷,因此叫做平截頭體。

平截頭體內部的所有元素都是潛在可見的,至少部分可見。所以沒有必要渲染那些平截頭體外部的元素,因爲它們根本看不到。

 

上圖中,所有綠色(完全在View的平截頭體內)和黃色(部分可見)的圖形都將被渲染。但是紅色的圖形不會被渲染。需要留意的是綠色的球體不可見,它被黃色橢圓遮擋住了,但它也會被渲染,因爲它在View的平截頭體內。

View平截頭體篩選目的就是要甄別什麼在平截頭體內部,篩選所有不在其內部的。只有其內部的實體被送往圖形硬件,最後請求硬件渲染它們,同時保存那些不可見的節點。由於3D世界中僅有可見節點駐留在顯存內,所以某種程度上提高了應用程序的性能。這更有希望描述整個3D世界。

如果平截頭體內這部分比整個場景小得多,那麼這個試驗就顯得意義非凡。但是小得多的標準是什麼呢?這取決於應用程序。極端的情況是整個場景總是完全可見,這時View平截頭體篩選只是浪費時間,因爲沒什麼可篩選的。幸運的是這種篩選技術非常容易實現,並且相比性能上的顯著提升,這個值得一試。

http://www.lighthouse3d.com/opengl/viewfrustum/index.php?intro

 

2 View平截頭體的形狀

 

在這一節裏,View平截頭體的形狀通過OpenGL程序教學來展開。假設用gluPerspective函數定義了一個透視投影,並且相機位置通過gluLookAt來設置。

首先,讓我們看看這些函數的參數(均爲float型):

l  gluPerspective(fov, ratio, nearDist, farDist);

l  gluLookAt(px, py, pz, lx, ly, lz, ux, uy, uz);

P(px, py, pz)是相機的位置,View射線通過d=L(lx, ly, lz)-P(px, py, pz)定義。遠近平面都垂直於View射線,它們距離相機位置的距離分別爲nearDistfarDist。其矩形邊界大小事距離和fovratioratio between the horizontal and vertical fields of view)的函數。

Hnear = 2 * tan(fov/2) * nearDist

Wnear = Hear * ratio

Hfar  = 2 * tan(fov/2) * farDist;

Wfar  = Hfar * ratio

 

爲了執行View平截頭體篩選,需要按下面兩步操作:

l  提取平截頭體的數據 每次平截頭體改變後都需要做,i.e.,相機或透視模式改變時。

l  根據平截頭體測試對象是否被篩選 這一步需要在每一幀執行。如果每個對象在每一幀之間保持篩選狀態不變,那麼僅僅需要在相機移動(比如平截頭體更新或透視模式改變)時做測試。

http://www.lighthouse3d.com/opengl/viewfrustum/index.php?defvf

 

3 幾何方法 提取平面

在自然空間中運用幾何方法,可以通過View平截頭體的數據計算定義frustum邊界的六個平面:near, far, top, bottom, left & right

這些平面的法向量(normal)指向View平截頭體內部。通過檢測對象在平面的哪一側,就可以確定一個對象是否在平截頭體內。具體可以計算點到平面的有符號距離(signed distance)來實現,如果它在法向量所指的一側,即距離爲正值,該對象在平面右側。如果一個對象位於六個平面的右側,則它在平截頭體內。

本節論述瞭如何計算定義平截頭體的六個平面的方法。Testing將在後面的詳述。

一種方法是,首先確定平截頭體的八個頂點,然後用這些頂點來定義六個平面。

下圖顯示了上面提及的頂點,它們可以用來計算這六個平面。

 

定義點的標記法是這樣的:第一個字母表示在near plane(n)還是far plane(f)上;第二個字母表示改點的top(t)bottom(b)位置;第三個字母表示left(l)right(r)位置。

回顧上一節提到的一些知識:

l  P - 相機位置

l  D - 相機View射線的矢量方向。這裏假設該向量已標準化。

l  nearDist - 相機到near plane的距離

l  Hnear - near plane的高

l  Wnear - near plane的寬

l  farDist - 相機到far plane的距離

l  Hfar - far plane的高

l  Wfar - far plane的寬

 

另外還需要一些單位向量,也就是up vectorright vector。前者通過單位化向量(ux, uy, uz)得到(即gluLookAt函數的後三個參數);後者通過計算up vectord vector的叉積得到。如下圖,顯示瞭如何得到far plane的左上角點ftl

 

具體可以通過下面的式子計算ftl點,

fc = p + d * farDist

ftl = fc + (up * Hfar/2) - (right * Wfar/2)

其他點這樣計算,

ftr = fc + (up * Hfar/2) + (right * Wfar/2)

fbl = fc - (up * Hfar/2) - (right * Wfar/2)

fbr = fc - (up * Hfar/2) + (right * Wfar/2)

 

nc = p + d * nearDist;

ntl = nc + (up * Hnear/2) - (right * Wnear/2)

ntr = nc + (up * Hnear/2) + (right * Wnear/2)

nbl = nc - (up * Hnear/2) - (right * Wnear/2)

nbr = nc - (up * Hnear/2) + (right * Wnear/2)

 

三個點可以定義一個平面。例如,可以用ftl, ftr, fbr定義far plane,但應該保持法向方向的一致性,所有法向都指向view frustum內部。

這種方法計算near/far planes可以做一些優化。一個平面可由一個法向和一個點定義,這些平面都可以基於相機的定義計算出來。near plane可由平面上的點nc和法向d定義,far plane可由-d和點fc定義。

其他平面也可以用一種更高效的方式計算出來,即利用一個法向量和一個點來定義平面。下面的代碼給出了right plane的法向量。

nc = p + d * nearDist

fc = p + d * farDist

a = (nc + right * Wnear/2) - p

a.normalize();

normalRight = up x a;

 

http://www.lighthouse3d.com/opengl/viewfrustum/index.php?gaplanes

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章