關於WebGL繪制線原理不明白的小伙伴,可以看看我之前的文章WebGL繪制有寬度的線。這一篇我們主要來介紹端頭的繪制,先看效果圖。

端頭一般被稱為lineCap,主要有以下三種形式:

butt最簡單等於沒有端頭,square一般是多出lineWidth/2的長度,round是一個以lineWidth/2為半徑的圓。一般情況下繪制lineCap的思路都是添加額外的三角形,如一些開元庫或者mapbox的方法,一般來說mapbox的方法已經可以了,但是我還是感覺頂點太多,甚至對square的情況都不願意在增加兩個頂點。

方法總是自己去探索的,在繪制寬度線中,我已經總結了自己的一套理論,將線距離映射成uv坐標的思路來繪制一些線的效果,那么在這里仍然沿着這種思路去思考。
對於square來說只要在繪制頂點的時候,在線的開頭和結尾分別做一定的偏移即可。對於round我們可以在suqare的基礎上,在片元着色器中做一些判斷,距離中心點距離大於lineWidth/2的像素全部拋棄掉。

所以對於線起點和終點處頂點的位置在上一篇文章的基礎上,做了沿着線方向的偏移
這樣的話我們就能繪制出square類型的lineCap。但是對於round僅僅這樣做還不行,還需要在片元着色器中根據中心點做一次剔除。那么問題來了,經過偏移后如何知道圓心點的位置,在來根據像素距離來進行剔除。這時候就輪到上篇文章利用紋理坐標來表示線長度的思路了,上一篇中我們把路線長度映射成從0-1的uv坐標,那么對於端頭來說,我們可以把超出的那一半線寬的像素映射成紋理坐標,可以想象這部分長度對於起點來說等於負的線長度,對應的紋理坐標就是負的紋理坐標;對於終點來說多出的這部分像素等於增加了的線長度,那么對應紋理坐標就是超過1的部分。那么接下來的問題就是如何把線上的像素長度對應於線的長度等於把像素與3d世界中的單位進行映射。
如果對投影矩陣不是很了解的同學,最好看看我的這篇文章webgl開發第一道坎——矩陣與坐標變換,這里我們需要用到投影矩陣的中的元素

resolution.x代表canvas顯示元素的寬度,這里恐怕有些地方不太好理解。n其實是代表相機的近平面,我們先假設為1;那么pixelWidthRatio表示像素與3d單位的一個比值的一半。后面我們先暫時也假設finalPosition.w的值也為一,那么最終vPixelWidth的值表示每像素代表多少3d單位。但是由於視錐體的投影變換並不是線性的,所以這樣得到的vPixelWidth並不適用視錐體中的所有地方。這時候我們回來看pixelWidthRatio有一個分母n代表近平面,finalPosition.w代表投影后的點距離相機坐標中的z值距離。w/n這里是想用線性來補充一部分非線性變換帶來的影響,讓vPixelWidth表示每像素代表多少3d單位這個結果盡量的准確。實際效果也確實可以達到預期。
現在我們可以將端頭多出的一半線寬的像素距離轉化成線的距離,同時在轉化成紋理單位。
接下來我們可以在片元着色器中進行剔除工作。這里我們需要做幾步工作:
- 紋理坐標轉換成線的距離長度
- 線的距離長度轉換成像素單位
- 對大於lineWidth/2長度的像素進行剔除
這里我們需要用到varying變量對vPixelWidth進行差值。最終我們繪制出round的lineCap效果。

