WebGL 繪製Line的bug(二)

基本思路
上一篇文章簡單介紹了WebGL繪製Line的bug,這一篇文章會講述解決這個問題的work around。

上一篇文章結尾簡單提了下解決的思路,就是通過三角形來模擬線條。

以兩個端點組成的線段爲例,繪製line的時候只用指定兩個端點,如果通過三角形來模擬一條線段,則至少需要兩個三角形,如下圖:

兩個三角形模擬的線段
因此要繪製一條線段,則需要六個頂點,兩個三角形;當時從上圖中,可以看出有些頂點是共享,實際上只需要四個頂點,然後通過索引的方式繪製兩個三角形,相信熟悉WebGL的同學都理解這種通過索引來繪製的方式,此處不詳細說明。

如果要繪製兩條相連的線段呢,則需要增加兩個頂點,也就是6個頂點,繪製四個三角形,依次類推,繪製三條相連的線段需要8個頂點,繪製6個三角形;由此可以得出一個結論,繪製有n個端點的Line,需要:2 n 個頂點, (n-1) 2個三角形。

對於一條線段而言,控制的參數實際上只有兩個端點的座標和線的寬度。

從上面的分析,我們知道了給定一系列點(n個)和線的寬度,繪製一條線段需要的頂點數是n * 2.

如何計算頂點
兩個端點的情況
當時 n個頂點數據應該如何計算得到呢? 先舉個簡單的2維繪圖的例子,現在假設給定了兩個端點:

(-50,0)和(50,0),要繪製一條寬度爲2的線條,那麼總共是四個頂點,第一個頂點是從第一個端點 + 線寬造成的偏移量,線寬爲2,所以偏移量的基數應該是2 / 2 = 1;

第二個頂點是從第一個端點 + 線寬造成的偏移量 (-1),同樣線寬爲2,所以偏移量的基數應該是2 /2 (-1) = -1;

依次類推,那麼應該如何偏移呢? 這個與線段的走向有關,示例中 線段的走向可以用第二個端點 - 第一個端點計算出來:

(50,0) - (-50,0) = (100,0) ,歸一化之後就是(1,0),此爲線段的方向向量,表示的線段的走向的是沿x軸正方向,對於第一頂點,偏移的方向應該是(1,0)逆時針旋轉90度,即和線段走向垂直的方向(與線段垂直的方向有兩個,此處基於右手法則,選擇逆時針旋轉90度的一個),旋轉90度之後,向量編程了(0,-1)

從圖形學裏面的數學知識可以得知,向量(x,y)逆時針旋轉90度變成(-y,x);

對於第二個頂點,偏移的方向應該是(1,0)順時針旋轉90度,但是前面,我們已經把偏移的基數變成-1了,所以可以認爲偏移的方向還是(1,0)逆時針旋轉90度,如圖:

基於線段方向計算頂點偏移方向
由此,可以得出第一個頂點的位置是:

(-50,0) - (0,-1)* 1 = (-50,-1),

第二個頂點的位置是:

(-50,0)-(0,-1) * 1 = (-50,1)

對於第三,第四個頂點的計算也是類似的。

多個端點的情況
上面討論的是隻有兩個端點的情況,事實上,如果是多個端點,以上討論的情況只適合多個端點中第一個端點和最後一個端點的情況,對於中間的端點,偏移的方向要綜合考慮這個端點連接的兩條線段的情況,同樣舉例說明:

假設三個端點的情況,三個端點 分別是 (-50,0),(0,0),(0,50),現在要計算第二個端點(0,0)對應的兩個頂點(第三、第四個),如圖:

三個端點
此時要計算中間的端點的兩個頂點位置,則需要考慮改端點連接的兩天線段的方向:

線段方向計算偏移方向
計算的大致思路,通過該端點的和前一個端點相減 計算出第一條線段的方向:

(0,0) - (-50,0) = (50,0) = (1,0)(歸一化)

在通過該該端點的下一個端點減去該端點計算出第二條線段的方向:

(0,50) - (0,0) = (0,50) = (0,1)(歸一化)

然後兩個方向向量相加,在旋轉逆時針旋轉90度,可以得到偏移的方向:

(1,0) + (0,1) = (1,1) = (0.707,0.707)(歸一化)

旋轉之後,偏移方向編程了(-0.707,0.707),

需要注意的是,此時的的偏移基數其實也是發生了變化的,拐角處的偏移量此時應該變成大了,即有了一個放大因子。 可以通過 1 / 偏移方向 點乘 第一條線段的方向 來獲取這個放大因子,不過如果兩條線段夾角很小,點乘的值也很小,放大因子很大,爲了拐角處的尖角不顯得是否大,我們一般限定放大因子不超過2. 因此公式可以變成:

1 / max(偏移方向 . 第一條線段的方向,0.5)

如何組織頂點數據
上面大量篇幅講述瞭如何計算頂點座標,事實上,前面文字所述的一切計算方法都是發生在頂點着色器中的,而且也只能在着色器中計算,因爲最終顯示到屏幕上的頂點與鏡頭相關,上文中只是簡單的用了2維的情況模擬,如果在js端計算,將極大消耗性能。 (那你不是瞎扯嗎,我們都還沒搞清楚如何計算出要傳遞給頂點着色器的數據呢),其實不是瞎扯,因爲只有搞清楚了在着色器中如何計算最終的頂點,才知道如何向頂點着色器中組織數據,

以上文中“多個端點的情況”的爲例,我們可以總結出計算出一個頂點需要哪些數據:

端點座標,偏移量,前一個端點座標,後一個端點座標

因此在着色器中需要定義四個attribute變量 position,offset,positionPrev,positionNext,分別用來接收端點座標,偏移量,前一個端點座標,後一個端點座標。

對於前面兩頂點,其端點沒有前一個端點,此時前一個端點就取端點座標,然後在着色器中判斷 如果前一個端點點座標 == 端點座標,則表明是第一個端點;使用兩個端點的情況計算。

低於後面兩個頂點,其端點沒有後一個端點,此時後一個端點就取端點座標,然後在着色器中判斷 如果後一個端點點座標 == 端點座標,則表明是最後一個端點;使用兩個端點的情況計算。

對於中間的頂點,既存在端點座標,也存在前一個端點的座標,和後一個端點的座標,就使用前面多個端點的情況計算。

還是以之前三個端點的例子爲例,三個端點的(50,0,0),(0,0,0),(0,50,0),線寬爲2(注意此時已經是三維座標了,之前模擬的情況是用屏幕上的2維座標來模擬頂點在着色器中通過透視變換變成了二維座標的情況)

那麼第一個頂點的四個變量的數據分別是:

端點座標, 偏移量, 前一個端點座標,後一個端點座標

(50,0,0),2/2, (50,0,0) (0,0,0)

第二個頂點的四個變量的數據分別是:

端點座標, 偏移量, 前一個端點座標,後一個端點座標

(50,0,0),-2/2, (50,0,0) (0,0,0)

第三個頂點的四個變量的數據分別是:

端點座標, 偏移量, 前一個端點座標,後一個端點座標

(0,0,0),2/2, (50,0,0) (0,50,0)

第四個頂點的四個變量的數據分別是:

端點座標, 偏移量, 前一個端點座標,後一個端點座標

(0,0,0),-2/2, (50,0,0) (0,50,0)

第五個頂點的四個變量的數據分別是:

端點座標, 偏移量, 前一個端點座標,後一個端點座標

(0,50,0),2/2, (0,0,0) (0,50,0)

第六個頂點的四個變量的數據分別是:

端點座標, 偏移量, 前一個端點座標,後一個端點座標

(0,50,0),-2/2, (0,0,0) (0,50,0)

到此爲止,我們知道了如何組織繪製需要的頂點的數據。

下一篇將貼上相關代碼說明。

歡迎關注本人的公衆號:ITman彪叔

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