摘要:那是因為,線條的光柵化過程和多邊形的光柵化過程并不是完全一致的。這將會導致一些本該被隱藏的線段,未被遮擋。原理我們知道,一般對象都是由三角形組成的。其中涉及到和的相關介紹,筆者將會在后續的文章中介紹。
如果要把一個對象的線框繪制出來,一般的方法是先繪制實體對象,然后通過gl.LINES的模式再繪制一遍模型,此時模型的線框就會被繪制出來。
gl.LINES的問題此方法需要繪制兩遍對象,因此會造成性能的損失。
使用此種方式繪制線框的時候,深度值偏移是必要的。那是因為,線條的光柵化過程和多邊形的光柵化過程并不是完全一致的。這就導致繪制一個多邊形的邊和繪制多邊形本身,相同位置的片元,其深度值可能并不一樣。
線段和多邊形的光柵化不完全一致,為了避免z-fighting,也需要一個深度偏移。
但是,添加一個偏移并不能完美的解決問題。 這將會導致一些本該被隱藏的線段,未被遮擋。
使用gl.LINES的另外一個問題是,在WebGL中,存在一個bug,就是線寬只能設置為1。請參考以下文章:
https://www.jianshu.com/p/cee... 繪制Line的bug(一)
因此本文將會介紹一種方法,可以在一個pass內繪制對象及其線框。
原理我們知道,一般對象都是由三角形組成的。而要顯示的線框,正好是三角形的邊,如果在繪制的時候,給三角形的邊一個不同的顏色,便可以實現在對象上面繪制線框的效果。
那么現在的問題是,如何確定三角形的邊呢?
要確定三角形的變,可以使用重心坐標系。有關重心坐標的的說明,可以參考:
https://zh.wikipedia.org/wiki...
對于三角形而言,重心坐標可以這樣定義:
三角形所在平面上的任意一點P(笛卡爾坐標),可以通過三角形的三個頂點A、B、C(笛卡爾坐標)來表示:
P = Ax + By + C * Z,其中(x、y、z)便是重心坐標。由此可以看出P點其實是A、B、C點加權之和。
如下圖所示,A點的重心坐標是(1,0,0),B點的重心坐標是(0,1,0),C點的重心坐標是(0,0,1)
由上面的講解 和圖片展示可以得知,重心坐標(x,y,z)中任何一個值為0的點,都在三角形的邊上。不過在實際的圖形渲染中,邊的寬度不可能是0,而應該是一個大于0的值,所以一般可以指定一個要繪制的線寬width,如果任何一個點的重心坐標(x,y,z)中的人一個分量的值小于這個線寬width,可以認為在邊上。
代碼實現基于上面說的原理,首先需要定義頂點的重心坐標,對于一個三角形來說,可以把三個頂點的中心坐標分別指定為(1,0,0)、(0,1,0)、(0,1,0)即可。而對于一個四邊形,有四個頂點 0,1,2,3,而繪制的時候使用索引 0,1,2, 2,1,3來繪制,此時可以把重心坐標定義如下:
var barycentric = [ 1,0,0, 0,1,0, 0,0, 1, 1,0,0, ];
然后,在著頂點色器中定義對應的attribute變量,由于重心坐標最終需要傳遞到片元著色器中,所以還需要對應的varying變量:
attribute vec3 aBarycentric; ... varying vec3 vBarycentric; void main(){ ... vBarycentric = aBarycentric; }
然后,在片元著色器中判斷,如果vBarycentric的任意一個分量的值小于指定的寬度值,則顏色為指定的線框顏色:
if(any(lessThan(vBarycentric, vec3(0.02)))){ gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); } else{ gl_FragColor = color; }
通過上面代碼,最終繪制的立方體效果如下:
從上面的立方體繪制的效果圖可以看出,線框的鋸齒很嚴重,而且線的寬度不是一致的。這是因為,之前的判斷是基于三角形表面的,通過光柵化之后,由于線條的角度等原因,最終在屏幕上面的寬度就變得不一致了。
使用fwidth方法要線寬的判斷基于屏幕,需要使用到一個方法fwidth。該方法需要WebGL 引入一個擴展之后才能使用。該擴展是:OES_standard_derivatives。
首先使用WebGL的getExtension方法獲取該擴展,代碼如下:
gl.getExtension("OES_standard_derivatives");
然后在shader中啟用這個擴展,代碼如下:
#extension GL_OES_standard_derivatives : enable
然后通過fwidth函數,把vBarycentric的值縮放到 vBarycentric在屏幕變化的范圍之內,代碼如下:
vec3 d = fwidth(vBarycentric); vec3 a3 = smoothstep(vec3(0.0), d*2.0, vBarycentric);
fwidth表示一個變量在屏幕空間的x軸變化dFdx + y軸變化dFdx之和。 其中涉及到dFdx、dFdy和fwidth的相關介紹,筆者將會在后續的文章中介紹。
在獲取了基于屏幕像素空間的的重心坐標a3之后,變可以通過通過該變量來進行判斷,并繪制出指定寬度的線框:
gl_FragColor.rgb = mix(vec3(0.0,0.0,0.0), vec3(1.0), min(min(a3.x, a3.y), a3.z));
通過改良之后的繪制效果如下,可以看出明細效果改進了很多:
前面我們看到的都是三角形的線框,有的時候,我們希望獲取四邊形的線框,應該怎么處理呢? 其實此時,只需要調整下頂點的重心坐標即可,在前文中,一個四邊形的四個頂點的重心坐標如下:
var barycentric = [ 1,0,0, 0,1,0, 0,0, 1, 1,0,0, ];
如果把四邊形的四個頂點的重心坐標調整如下:
var barycentric = [ 1,0,0, 1,1,0, 0,0, 1, 0,0,0, //前 ];
便可以達到效果,最終繪制的效果如下圖所示:
英偉達也提出過繪制線框的解決方案,參考下面鏈接:
https://developer.download.nvidia.com/SDK/10/direct3d/Source/SolidWireframe/Doc/SolidWireframe.pdf
不過該方案是基于Geometry Shader技術來實現的,而該技術在WebGL并未實現,所以該方案不能很好的移植到到WebGL。
如果獲取完整源代碼,請關注公眾號。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/97119.html
摘要:偏導數函數可以用于片元著色器中的任何變量。偏導數和用于計算紋理的一些列的子圖,每個子圖都比前一個的尺寸縮小了倍。在紋理取樣過程中使用偏導數來選擇最佳的級數。 本文適合對webgl、計算機圖形學、前端可視化感興趣的讀者。 偏導數函數(HLSL中的ddx和ddy,GLSL中的dFdx和dFdy)是片元著色器中的一個用于計算任何變量基于屏幕空間坐標的變化率的指令(函數)。在WebGL中,使用...
摘要:一般說來,對于制圖建模軟通常使正交投影,這樣不會因為投影而改變物體比例而對于其他大多數應用,通常使用透視投影,因為這更接近人眼的觀察效果。 showImg(https://segmentfault.com/img/remote/1460000012581680?w=1920&h=1080); 1. 概述 1.1 什么是WebGL? WebGL是在瀏覽器中實現三維效果的一套規范 想要使用...
摘要:導言立體視覺技術前幾年研究極為廣泛,研究生期間也以此為課題,主要學習并研究特征提取和圖像匹配方向目前火遍全球的虛擬現實技術,也屬于立體視覺領域的研究范疇。 導言 立體視覺技術前幾年研究極為廣泛,研究生期間也以此為課題,主要學習并研究特征提取和圖像匹配方向;目前火遍全球的虛擬現實技術,也屬于立體視覺領域的研究范疇。 立體視覺技術可研究內容很多,實現一整套立體視覺呈現步驟如下: 攝像機標...
閱讀 2727·2021-11-22 15:22
閱讀 1631·2021-11-22 14:56
閱讀 3616·2021-09-22 15:12
閱讀 2403·2021-09-02 15:41
閱讀 2122·2021-08-27 16:26
閱讀 1113·2019-08-30 15:55
閱讀 2139·2019-08-29 17:30
閱讀 665·2019-08-29 16:26