WebGL入門與進階1
改革開發40年以來,世界日新月異,無論從生活到精神上都有了顛覆性的變化,曾經教授還是教書的,磚家還叫專家,太陽還不叫日,菊花還是一種花,老王還沒那么多,Web還只需要做IE,XHR還沒出生的時候,怎么能想到現在瀏覽器會提供如此豐富多彩的多媒體生活,無論是音頻、視頻、以及各種漂亮的頁面都在讓用戶更好的擁抱著互聯網,當二維頁面無法滿足用戶之后,會出現什么樣的內容來繼續推進Web進展呢,沒錯,就是3D,瀏覽器中看到的內容從平面變成3D的時候,oH,My God,提起來都讓人興奮。
網站的未來是這樣子的:
的確,3D技術會讓平淡的網頁變的更酷,更讓人眼花撩眼,這本后隱藏著什么呢?來來,咱們做下來泡一壺茶,邊喝邊聊,這個神奇的家伙叫做 WebGL 。
談起WebGL可能有一些人比較陌生,實際上WebGL是一種3D繪圖標準,這種繪圖技術標準允許把JavaScript和OpenGL ES 2.0結合在一起,通過增加OpenGL ES 2.0的一個JavaScript綁定,WebGL可以為HTML5 Canvas提供硬件3D加速渲染,這樣Web開發人員就可以借助系統顯卡來在瀏覽器里更流暢地展示3D場景和模型了,還能創建復雜的導航和數據視覺化。顯然,WebGL技術標準免去了開發網頁專用渲染插件的麻煩,可被用于創建具有復雜3D結構的網站頁面,甚至可以用來設計3D網頁游戲等等。
此鏈接可以查看你的游覽器是否支持WebGL以及支持的版本[1]。
看WebGL的背景實際上是JavaScript操作一些OpenGL接口,也就意味著,可能會編寫一部分GLSL ES 2.0的代碼,沒錯,你猜對了,WebGL只是綁定了一層,內部的一些核心內容,如著色器,材質,燈光等都是需要借助GLSL ES語法來操作的.
基于WebGL周邊也衍生了眾多的第三方庫,如開發應用類的Three.js,開發游戲類的Egert.js等,都大大的降低了學習WebGL的成本,但是本著有問題解決問題,沒問題制造問題在解決問題的程序猿態度,還是覺得應該稍微了解一下WebGL一些基本的概念,以便能更好的去理解不同框架帶來的便捷以及優勢。
接下來先簡單介紹一下使用到的知識要點。
創建webGL對象
不同瀏覽器生命WebGL對象方式有所區別,雖然大部分瀏覽器都支持experimental-webgl,而且以后會變成webgl,所以創建時做一下兼容處理
var canvas = document.getElementById("glcanvas");
gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
著色器
WebGL依賴一種新的稱為著色器(shader)的繪圖機制。著色器提供了靈活且強大的繪制二維或三維圖形的方法,所有WebGL必須使用它。著色器不僅強大,而且更復雜,僅僅通過一條簡單的繪圖指令是不能操作它的。
WebGL需要兩種著色器
- 頂點著色器(Vertex shader):頂點著色器是用來描述頂點特性(如位置、顏色等)的程序。 頂點(Vertex) 是指二維或三維空間的一個點,比如二維或三維空間線與線之間的交叉點或者端點。
- 片元著色器(Fragment shader):進行逐片元處理過程(如光照等)的程序。 片元(fragment) 是一個WebGL的術語,你可以將其理解成像素。
著色器語言使用的是GLSL ES語言,所以在javascript需要將之存放在字符串中,等待調用編譯
創建頂點著色器:
var VSHADER_SOURCE =
'void main() {\n' +
' gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n' +
' gl_PointSize = 10.0;\n' +
'}\n';
創建片元著色器:
var FSHADER_SOURCE =
'void main() {\n' +
' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' +
'}\n';
瀏覽器的整個過程如下:
著色器中包含幾個內置變量:gl_Position、gl_PointSize、gl_FragColor。
著色器語言中涉及到vec4的數據類型,此數據類型是一個思維浮點數組,所以其值不可以是整形如(1,1,1,1), 正確應為:(1.0,1.0,1.0,1.0)
- gl_Position : 為一種vec4類型的變量,且必須被賦值。四維坐標矢量,我們稱之為 齊次坐標 ,即(x,y,z,w)等價于三維左邊(x/w,y/w,z/w),w相當于深度,沒有特殊要求設置為1.0即可。
- gl_PointSize :表示頂點的尺寸,也是浮點數,為非必填項,如果不填則默認顯示為1.0。
- gl_FragColor :該變量為片元著色器唯一的內置變量,表示其顏色,也是一個vec4類型變量,分別代表(R,G,B,A),不過顏色范圍是從 0.0-1.0 對應Javascript中的 #00-#FF 。
有了著色器我們就可以著手去繪制圖像了,既然繪制3D圖形,必然會有對應的三維坐標系,WebGL采用右手坐標系,如圖所示:
使用著色器
讓我們來看看如何把著色器代碼編譯并且使用起來
著色器代碼需要載入到一個程序中,webgl使用此程序才能調用著色器。
var program = gl.createProgram();
// 創建頂點著色器
var vShader = gl.createShader(gl.VERTEX_SHADER);
// 創建片元著色器
var fShader = gl.createShader(gl.FRAGMENT_SHADER);
// shader容器與著色器綁定
gl.shaderSource(vShader, VSHADER_SOURCE);
gl.shaderSource(fShader, FSHADER_SOURCE);
// 將GLSE語言編譯成瀏覽器可用代碼
gl.compileShader(vShader);
gl.compileShader(fShader);
// 將著色器添加到程序上
gl.attachShader(program, vShader);
gl.attachShader(program, fShader);
// 鏈接程序,在鏈接操作執行以后,可以任意修改shader的源代碼,
對shader重新編譯不會影響整個程序,除非重新鏈接程序
gl.linkProgram(program);
// 加載并使用鏈接好的程序
gl.useProgram(program);
讓我們嘗試繪制一個點
gl.clearColor(0.0,0.0,0.0,1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.POINTS, 0 ,1);
我們來看一看最終結果,果然出來了一個點。
Why? 咋這么模糊?沒錯不是你的眼鏡度數又高了,的確是模糊的。
讓我們來說說WebGL的坐標系。
因為WebGL的坐標系與實際頁面中的坐標系是不同的,如下圖,普通canvas坐標系與正常的瀏覽器像素值相同,但WebGL中的坐標系是以整個WebGL中心點為(0.0,0.0),而且坐標的精確度為小數點后一位。坐標系對比如下圖所示:
圖上的實例是在使用源生WebGL時,并且未對視角有設置的情況下的默認值。默認視角的位置為(0.0, 0.0, 0.0),并且lookAt(0.0, 0.0, -1.0),也就是默認的視角是看向z軸負坐標的,如果點在z的正位置上,則是無法看到點的。
繪制之后,發現這個依舊會繪制出超級模糊的圖像,那是因為整個WebGL的尺寸是與canvas寬度與高度相關連的,并且canvas的寬度與高度如果用css來設置的話,會被默認成100×100,也就意味著,你繪制出來的圖形是把100×100的圖形拉伸到當前canvas的尺寸中。所以正確的設置canvas的方式應該如下:
//錯誤
<canvas id="glcanvas" style="width: 700px; height: 500px;">
//正確的方式
<canvas id="glcanvas" width="700" height="500">
一個真實尺寸的清晰的點就出現了。
但我們辛苦繪制出來的點居然是正方形的,但WebGL是未提供繪制圓點的方法。
我們首先來了解一下WebGL的渲染機制,頂點著色器的信息在傳遞給OpenGL底層繪制的時候,會先進行光柵化,也就是把點轉化成對應的像素。然后在片元著色器會逐個點進行渲染,最終就達到了視覺看到的效果。
點也是一樣,會將點轉變成多個像素點,所以要變成圓點,需要如下的方式:
我們需要在片元著色器中來處理,將非原型區域的像素點,不用片元著色器來繪制,著色器需要判斷距離圓點的位置超過0.5的話,就忽略此片元點,最終就會出現一個圓點的效果。
var FSHADER_SOURCE = `
#ifdef GL_ES
precision mediump float;
#endif
void main() {
float d = distance(gl_PointCoord, vec2(0.5,0.5));
if(d < 0.5){
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}else{ discard;}
}`;
如下圖,我們繪制了一個圓點:
本章我們已經了解了WebGL的一些背景,以及如何使用一些基本功能,那么如何把著色器動態化并且同時高性能的繪制大量點呢?下一章我們會解開WebGL緩存區的面紗。
來自:https://jdc.jd.com/archives/212363