1. 為什麼要背面消隱
通過之前的DEMO,能夠知道如果在渲染過程中多邊形越多,那麼要不處理的內容就越多,就越消費電腦的處理能力。對於物體來說,一般我們只看到它面對我們的面,可能不是正對著,但是肯定有很多面是完全背對我們的,我們就應該在渲染時跳過他們。這將減少大約一半的三角形渲染量。原理其實很簡單,就是向量與平面的關係,這個原理我在參數化直線與平面的那篇文章中特意推導了整個來龍去脈。
我喜歡在2D俯視圖中來分析問題,如:
2. 注意事項一:背面測試的是“視線與面朝向”的關係,而與相機朝向不直接相關
在剛接觸背面消隱時,我以為是拿相機的朝向向量和面的法向量來求夾角,雖然也可以消除一些面,但發現是不對的,因為存在下面的這種情況:
上面紅色的面和綠色的面的法向量是相同的,但是一個面是正面,一個面是背面。因為相機朝向是固定的,所以對於這種情況,相機朝向與面法向量求夾角而得出的結論是不正確的。
在圖中可以發現,如果我們任取面上一點,與相機的位置做連線,那麼這種情況就可以被區分出來。因為視線向量與法向量的夾角不同,一個是銳角,一個是鈍角。(乍看都是銳角,把他們的起始點都移到原點就能看出區別了)
而且可以注意到,無論取面上的哪一點來做連線都是正確的。
3. 注意事項二:求面法向量的結果與“頂點的儲存順序”息息相關,各面的頂點儲存順序必須一致
在做背面消隱時有一段時間怎麼調都調不對,我覺得肯定沒問題的東西,怎麼會不對呢?
於是我在紙上推算了半天,發現一個致命的錯誤——我每個三角形的頂點儲存順序是任意的,造成法向量的不一致性。
看:
如果求一個面的法向量,最簡單的方法是得到兩個向量,然後把它們做叉乘。比如上面的三角形OAC來說,可以用點A減去點O,得到OA向量。點C減點O得到OC向量,然後用OA×OC,得到法線向量,這裡隱含了一個重要的數學性質,那就是叉乘不滿足交換律。
即:OA×OC != OC×OA。
因為當我們拿到三個頂點的時候,是不知道這三個頂點怎麼排序的,那麼如果某個面1是逆時針來儲存的,如:O-A-C,又有某個面是順時針來儲存的,如O-C-A,那麼在求叉乘時,結果會截然不同。
所以對於各個面的頂點儲存時,一定要約定好順序,比如都進行下面的步驟:
1) 從物體的外面來看這個面,我們正對這個面
2) 逆時針來儲存這些點(順時、逆時無所謂,但所有面應統一)
樣本1:
對於面OAC,我們首先在物體的外面來看這個面,而不是在物體內部來看。然後逆時針來儲存這些點,則儲存順序為:O-A-C
樣本2:
對於面ABC,我們首先在物體的外面來看這個面。然後逆時針來儲存這些點,則儲存順序為:A-B-C,而不是A-C-B
正是因為這個原理,所以如果把3ds max的物體匯出之後,就可以發現3ds儲存的物體面中的頂點儲存順序都是滿足上面一致性約定的。
4. 注意事項三:頂點儲存順序、向量叉乘順序、視線向量的微妙關係
如果要寫出正確的背面測試函數,就必須要把這三因素的關係搞明白,因為有一個不正確,你的結果就是相反的。(如果有兩個搞錯,反而會是正確的,有趣吧,但是可能計算的法向量是錯誤的,而影響以後的光照計算,所以我們還是要把這個搞清楚)。
這裡給出一個正確的樣本,可以有所體會:
1) 頂點儲存順序約定:從物體外面來看,逆時針儲存
2) 在準備做叉乘時,可以拿到已經排好序的3個頂點(這裡假設為p0,p1,p2),統一約定使用u = p1 - p0,v = p2 - p0,也就是說從u轉到v,也是一個逆時針旋轉。因為p0->p1->p2是逆時針。
3) 求u×v,得到面法向量n,因為點的儲存的統一性和u->v的轉向相同,所以這時的面法向量是正確的,是朝向物體外面的。
4) 視線向量view用相機座標減去面上任意一點,這時這個向量是從三角面指向相機的。
5) 求n與view的點積,如果點積大於0,則為銳角,是正面,如果點積小於0,則為鈍角,是背面。這個性質的推導在向量函數庫的那篇文章中有介紹。
5. 總結
至此我們的背面消隱函數終於是正確的了。在《3D大師》這本書中,雖然他代碼是正確的,可是翻譯出來的解釋真的有時讓人摸不著頭腦,以後有時間還是看原版吧~這次如果我不是重新推導了一遍,光看那本書所說的文字真的是不會搞明白這裡面的玄機。而且這次對寫BLOG的認識又加深了,在寫這篇文章的同時我又發現了新的認識,又發現了代碼中隱藏在其中的邏輯錯誤,真爽。
6. 代碼下載
這次的代碼修改了上次的幾個錯誤。
1) 所有的頂點使用統一逆序的儲存
2) 使用視線而不是相機朝向來判斷
項目完整原始碼下載:>>點擊進入下載頁<<
效果和上次差不多,就不了。