WebGL微信賽車源碼出售繪製有端頭的線

關於WebGL微信賽車源碼出售Q3266397597【dashengba.com大聖源碼論壇】【bbsapple.com蘋果源碼論壇】繪製線原理不明白的小夥伴,可以看看我之前的文章WebGL繪製有寬度的線。這一篇我們主要來介紹端頭的繪製,先看效果圖。

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

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

  方法總是自己去探索的,在繪製寬度線中,我已經總結了自己的一套理論,將線距離映射成uv座標的思路來繪製一些線的效果,那麼在這裏仍然沿着這種思路去思考。

  對於square來說只要在繪製頂點的時候,在線的開頭和結尾分別做一定的偏移即可。對於round我們可以在suqare的基礎上,在片元着色器中做一些判斷,距離中心點距離大於lineWidth/2的像素全部拋棄掉。

  

  所以對於線起點和終點處頂點的位置在上一篇文章的基礎上,做了沿着線方向的偏移

複製代碼
// flag -1代表線起點, 1代表線終點;同時爲了防止一些精度問題,同樣使用紋理值來做一些判斷,讓真正的起點和終點做偏移,防止其中的一些小碎線段影響結果
' vec2 vertical_offset = normal lineWidth realSide 0.5;',
' vec2 horizontal_offset = vec2(0.0, 0.0);',
' if (flag == -1.0 && (uv.x == 0.0 || uv.x == 1.0)) {',
' horizontal_offset = -dir
lineWidth 0.5;',
' } else if (flag == 1.0 && (uv.x == 0.0 || uv.x == 1.0)) {',
' horizontal_offset = dir
lineWidth * 0.5;',
' }',
' if (useLineCap <= 0.0) {',
' horizontal_offset.xy = vec2(0.0, 0.0);',
' vUV.x = uv.x;',
' }',
' vec2 pos = currentP + vertical_offset + horizontal_offset;',
' gl_Position = unproject(pos, finalPosition.z, finalPosition.w);',
複製代碼
  這樣的話我們就能繪製出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單位這個結果儘量的準確。實際效果也確實可以達到預期。

複製代碼
' float aspect = resolution.x / resolution.y;', // 屏幕寬高比
' float pixelWidthRatio = 1. / (resolution.x projectionMatrix[0][0]);', // (r-l)/(2nWidth)
'',
' vColor = vec4( color, opacity );',
' vUV = uv;',
'',
' vec4 finalPosition = transform(position);',
' vec4 prevPos = transform(previous);',
' vec4 nextPos = transform(next);',
'',
' vec2 currentP = project(finalPosition);',
' vec2 prevP = project(prevPos);',
' vec2 nextP = project(nextPos);',
'',' float pixelWidth = finalPosition.w pixelWidthRatio;',
' vPixelWidth = pixelWidth
2.0;',
複製代碼
  現在我們可以將端頭多出的一半線寬的像素距離轉化成線的距離,同時在轉化成紋理單位。

複製代碼
' vec2 horizontal_offset = vec2(0.0, 0.0);',
' if (flag == -1.0 && (uv.x == 0.0 || uv.x == 1.0)) {',
' horizontal_offset = -dir lineWidth 0.5;',
' float radio = length(horizontal_offset) vPixelWidth / repeat.x;',// repeat.x代表線的距離長度
' vUV.x -= radio;',
' } else if (flag == 1.0 && (uv.x == 0.0 || uv.x == 1.0)) {',
' horizontal_offset = dir
lineWidth 0.5;',
' float radio = length(horizontal_offset)
vPixelWidth / repeat.x;',
' vUV.x += radio;',
' }',
複製代碼
  接下來我們可以在片元着色器中進行剔除工作。這裏我們需要做幾步工作:

紋理座標轉換成線的距離長度
線的距離長度轉換成像素單位
對大於lineWidth/2長度的像素進行剔除
複製代碼
'varying float vPixelWidth;',
'',
'void main() {',
'',
' vec4 c = vColor;',
' float uvx = vUV.x repeat.x;',
' vec2 coord = vec2(0.0, 0.0);',
// ' float horizontal = 0.0;',
// ' float vertical = 0.0;',
' if (uvx < 0.0) {',
' coord.x = abs(uvx) / vPixelWidth;',
' coord.y = abs(vUV.y - 0.5)
lineWidth;',
' } else if (uvx > repeat.x) {',
' coord.x = abs(uvx - repeat.x) / vPixelWidth;',
' coord.y = abs(vUV.y - 0.5) lineWidth;',
' }',
' if (length(coord) > lineWidth
0.5) {c.a = 0.0;}',
// ' float uvx = vUV.x * repeat.x;',
// ' if (uvx < 0.0 || uvx > repeat.x) {c.x=1.0;c.y=0.0;c.z=0.0;c.a = 1.0;}',
// ' if( c.a < alphaTest ) c.a = 0.0;',

' gl_FragColor = c;',
// ' gl_FragColor.a *= step(vCounters,visibility);',
'}' ];
複製代碼
  這裏我們需要用到varying變量對vPixelWidth進行差值。最終我們繪製出round的lineCap效果。

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