在OpenGL中,指定三角形圖元的頂點順序很重要。譬如,我們可以使用glFrontFace指定順時針還是逆時針為正面,然后通過glCullFace不繪制正面或者背面.
但你有沒有想過,OpenGL是怎么判斷三角形的頂點順序是順時針還是逆時針呢?
二維還是三維
首先我們先明確一下,culling這個階段發生在viewport transformation之后,rasterization之前. 三維頂點經過MVP變換->clipping->透視除法->NDC->viewport transformation,得到的是屏幕坐標系下的二維頂點.
這樣事情變得稍微簡單了一些,我們只需要考慮三個二維頂點的順序.
根據面積判斷頂點順序
多邊形的面積公式為:\(a = \frac{1}{2}\sum_{i=0}^{n-1}x_{i}y_{i\oplus 1} - x_{i\oplus 1}y_{i}\)
其中 \(i\oplus 1\) 表示 \((i+1)\) mod \(n\),用C語言描述:(i+1)%n.
將此公式應用到三角形,得:\(a = \frac{1}{2}(x_{0}y_{1} - x_{1}y_{0} + x_{1}y_{2} - x_{2}y_{1} + x_{2}y_{0} - x_{0}y_{2})\),作圖來解釋一下這個公式.
如圖所示,\(\Delta ABC\)的面積,可以看作\(\Delta AOC\)的面積加上\(\Delta BOC\)的面積,然后減掉\(\Delta AOB\)的面積. 即:
\(S_{\Delta ABC} = -S_{\Delta AOB} + S_{\Delta BOC} + S_{\Delta AOC}\)
而三角形的面積恰恰是兩條鄰邊向量叉乘的一半(帶有正負號). 故有:
\(-S_{\Delta AOB} = \frac{1}{2}(\vec{OA} \times \vec{OB}) = \frac{1}{2}(x_{0}y_{1} - x_{1}y_{0})\)
\(S_{\Delta BOC} = \frac{1}{2}(\vec{OB} \times \vec{OC}) = \frac{1}{2}(x_{1}y_{2} - x_{2}y_{1})\)
\(S_{\Delta AOC} = \frac{1}{2}(\vec{OC} \times \vec{OA}) = \frac{1}{2}(x_{2}y_{0} - x_{0}y_{2})\)
將三者相加,就相當於四邊形\(OBCA\)的面積減去\(\Delta AOB\)的面積,恰好就是\(\Delta ABC\)的面積,且這個面積是正的. 圖示的三角形是頂點順序是逆時針順序. 同學們可以自行驗證一下. 把順序變成順時針時,得到的面積是逆時針面積的相反數.
所以通過三角形面積的正負號判斷其頂點順序是可行的.
多邊形面積
凸多邊形應用上述公式也是可行的,在此不再贅述. 下面我們主要看一下凹多邊形的面積是否也能滿足此公式.
我們求的是凹多邊形\(ABCD\)的面積. 純色的三角形面積總能被公式叉乘運算得到的兩個三角形面積抵消掉. 例如\(S_{\Delta FOB}\)可以通過\(S_{\Delta AOB}\)和\(S_{\Delta OBC}\)抵消掉,\(S_{\Delta CGD}\)可以通過\(S_{\Delta ODA}\)和\(S_{\Delta ODC}\)抵消掉. 所有的純色三角形面積抵消之后,得到的就是凹多邊形的面積.
小結
- 通過\(a = \frac{1}{2}\sum_{i=0}^{n-1}x_{i}y_{i\oplus 1} - x_{i\oplus 1}y_{i}\)來求多邊形的面積,此面積有正負號
- 通過面積的正負號來判斷多邊形頂點的順序是順時針還是逆時針
- 上述公式也可以應用到凹多邊形
