1. 程式人生 > >WebGL 繪制Line的bug(二)

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彪叔

WebGL 繪制Line的bug(二)