上次咱們介紹瞭如何在<canvas>中使用WebGL,以及幾個基礎的WebGL函數;實現了背景色的重置;爲了擴展方便,咱們把上次的代碼作了些改動,將繪製圖形的js獨立成文件,這樣咱們只關注與這個js文件的編寫;之後除非HTML文件發生變化,咱們就跳過它,直接討論JavaScript代碼。html
1 <!doctype html>
2 <html>
3 <head>
4 <meta charset="UTF-8">
5 <meta name="Generator" content="EditPlus®">
6 <meta name="Author" content="Mirror">
7 <meta name="Keywords" content="">
8 <meta name="Description" content="">
9 <title>Hello Point</title>
10 <!--《WebGL編程指南》的做者爲讀者編寫的WebGL輔助函數-->
11 <script src="lib/webgl-utils.js"></script>
12 <script src="lib/webgl-debug.js"></script>
13 <script src="lib/cuon-utils.js"></script>
14 <!--JavaScript文件,在<canvas>中繪製圖形-->
15 <script src="lib/hello-point.js"></script>
16 </head>
17 <body onload="main()">
18 <!--定義<canvas>標籤,經過width屬性和height屬性規定它是一片400×400的繪製區域-->
19 <canvas id="myCanvas" width="400" height="400">
20 <!--當瀏覽器不支持時,會直接忽略<canvas>標籤,而直接顯示下面這一行提示-->
21 Please use the browser supporting "canvas". 22 </canvas>
23 </body>
24 </html>
接下來,咱們在此基礎上,繪製一個位於原點(0.0,0.0,0.0)處的10個像素大的紅色的點。由於使用的是三維圖形上下文,因此指定這個點時須要使用三維座標。座標系統後面介紹,這裏只須要理解爲原點位於<canvas>中心位置。效果如以下:java
實際上,咱們使用矩形而不是圓來繪製一個點,由於繪製矩形比繪製圓更快;就像在前一次中咱們以RGBA的形式指定了背景色同樣,這裏也須要一樣的處理;在前面,咱們使用2d上下文來繪製了一個矩形;先指定了繪圖顏色,而後進行繪製。你可能認爲WebGL也差很少,不幸的是,沒那麼簡單。WebGL依賴於一種新的稱爲着色器的繪圖機制。着色器提供了靈活且強大的繪製二維或三維圖形的方法,全部WebGL必須使用它。正由於強大,因此更復雜。web
要使用WebGL繪圖,必須使用着色器,哪怕是一個點(矩形);着色器程序是以字符串的形式「嵌入」在JavaScript文件中,而且在程序開始運行前就已經設置好了。WebGL須要使用兩種着色器:頂點着色器、片源着色器;下面分別介紹:編程
頂點着色器:頂點着色器是用來描述頂點特性(如位置、顏色等)的程序。頂點是指二維或三維空間中的一個點,好比二維或三維圖形的端點或交點。canvas
片元着色器:進行逐片元處理過程(如光源)的程序。片元是一個WebGL術語,你能夠將其理解爲像素(圖像的單元)。數組
在後續,咱們會詳細的學習着色器。簡單的說,在三維場景中,僅僅用線條和顏色把圖形畫出來是遠遠不夠的。你必須考慮如光線照上去後,或者觀察者的視角發生變化時,對場景會有什麼影響。着色器能夠高度靈活的完成這些工做;提供各類渲染效果。這也就是如今製做的三維場景如此逼真的緣由。瀏覽器
上圖顯示了WebGL系統的執行流程;左側爲瀏覽器,首先執行JavaScript程序,調用了WebGL的相關方法,而後頂點着色器和片元着色器就會執行,頂點着色器指定繪製圖形的位置和尺寸;片元着色器則指定繪製圖形的顏色;而後在顏色緩衝區內進行繪製,這時就清空了繪圖區,最後,顏色緩衝區中的內容就自動在瀏覽器的<canvas>標籤上顯示出來。ide
回到咱們今天的目標來,下面顯示了hello-point.js的代碼。函數
1 //頂點着色器程序 2 var VSHADER_SOURCE = 3 "void main() { \n" + 4 //設置座標 5 "gl_Position = vec4(0.0, 0.0, 0.0, 1.0); \n" + 6 //設置尺寸 7 "gl_PointSize = 10.0; \n" + 8 "} \n"; 9 10 //片元着色器 11 var FSHADER_SOURCE = 12 "void main() {\n" + 13 //設置顏色 14 "gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n" + 15 "}\n"; 16 17 function main() { 18 //獲取<canvas>標籤。 19 var canvas = document.getElementById("myCanvas"); 20 //獲取WebGL繪圖上下文。 21 var gl = getWebGLContext(canvas); 22 //若是瀏覽器不支持WebGL則提示錯誤。 23 if (!gl) { 24 console.log("Failed to get the rendering context for WebGL."); 25 return; 26 } 27 28 //初始化着色器 29 if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) { 30 console.log("Faile to initialize shaders."); 31 return; 32 } 33 34 //設置<canvas>的背景色 35 gl.clearColor(0.0, 0.0, 0.0, 1.0); 36 37 //清空<canvas> 38 gl.clear(gl.COLOR_BUFFER_BIT); 39 40 //繪製一個點 41 gl.drawArrays(gl.POINTS, 0, 1); 42 43 }
這個文件包含三個部分,頂點着色器程序(GLSL ES 語言),片元着色器程序(GLSL ES 語言)和主程序(JavaScript語言)。着色器程序代碼必須預先處理成單個字符串的形式,因此咱們用+號將多行字符串連成一個長字符串。每一行以\n結束,這是因爲當着色器內部出錯時,就能獲取出錯的行號,這對於檢查源代碼中的錯誤頗有幫助;可是,\n並非必須的。爲了更容易維護,也能夠把着色器代碼寫到單獨的文件中(就像javaScript文件同樣),而後經過javaScript程序從文件中讀取出來加載。學習
根據程序流程,加載頁面----->執行main()函數----->獲取<canvas>標籤----->獲取繪圖上下文;到這裏,都跟以前的流程同樣;接下來,會執行名爲initShaders()的函數。這個函數是《WebGL編程指南》的做者專門寫的一個輔助函數;該函數被定義在cuon.util.js中。這個函數的做用是對字符串形式的着色器進行初始化。咱們來看下這個函數的具體參數定義:
Web系統由兩部分組成,即頂點着色器和片元着色器。在初始化着色器以前,頂點着色器和片元着色器都是空白的,咱們須要將字符串形式的着色器代碼從JavaScript傳給WebGL系統,並創建着色器,這就是initShaders()函數要作的事情。着色器運行在WebGL系統中,而不是JavaScript程序中。
initShaders()函數執行成功後,着色器被建立好了並隨時可使用,頂點着色器將被首先執行,它對gl_Position變量和gl_PointSize變量進行賦值,並將它們傳入片元着色器,而後片元着色器再執行。實際上,片元着色器接收到的是通過光柵化(將幾何圖形變爲二維圖像的過程)處理後的片元值;如今能夠簡單認爲這兩個變量從頂點着色器傳入了片源着色器。
下面,咱們來看看着色器如何畫出一個點(矩形),前面提到,咱們須要三個信息來畫出這個點:位置,尺寸和顏色。位置和尺寸在頂點着色器中指定,和C語言同樣,着色器程序必須包含一個main()函數。main()前面的關鍵字void表示這個函數不會有返回值;並且也不能爲main()函數指定參數。着色器程序使用 = 操做符爲變量賦值。gl_Position和gl_pointSize這兩個變量內置在頂點着色器中,並且有着特殊的含義:前者表示頂點的位置,後者表示點的尺寸。gl_Position必須被賦值,不然着色器就沒法正常工做;gl_PointSize並非必須的,若是不賦值,着色器會爲其取默認值1.0。
和JavaScript不一樣,GLSL ES是一種強類型語言,也就是說,開發者須要明確指出某個變量是某種類型;相似於Java和C、C#等。在這個程序中,出現了兩種數據類型,float:表示浮點數;vec4:表示由4個浮點數組成的矢量(也稱做向量)。並且着色器語言沒有相似Java、C#語言的類型隱式轉換的功能,因此這樣寫:gl_PointSize = 10;就會致使錯誤;由於gl_PointSize 須要一個float類型的值,而10是整型。另外一個內置變量gl_Position的類型爲vec4,可是三維座標只有3個數,即X,Y和Z軸的座標值,這就須要某種方法將其轉化爲vec4類型的變量。着色器提供了內置函數vec4()能夠完成這個事情。就如咱們代碼中那樣使用:gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); 前3個份量對應X,Y,Z軸座標值,那第4個份量1.0是怎麼來的呢。由4個份量表示的座標被稱爲齊次座標,由於它可以提升處理三維數據的效率,因此在三維圖形系統中被大量使用。當第4個份量爲1.0時,這個齊次座標就能夠表示「前三個份量爲座標值」的點。
如前所述,片元能夠當作是顯示在屏幕上的像素。片元着色器和頂點着色器同樣,也有一個main()函數。片元着色器將點的顏色賦值給gl_FragColor,該變量是片元着色器惟一的內置變量,它控制着像素在屏幕上的最終顏色。對這個內置變量賦值後,相應的像素就會以這個顏色值顯示。和頂點着色器中頂點位置同樣。顏色值也是vec4類型的,分別表示RGBA的4個份量。
創建了着色器後,咱們開始進行繪製操做,咱們使用gl.DrawArray()函數進行繪製。這個函數的功能很是強大,能夠用來繪製各類圖形,具體參數說明以下:
由於咱們繪製的是單獨的點,因此設置第1個參數爲gl.POINTS;設置第2個參數爲0,表示從第1個頂點(雖然只有一個頂點)開始畫起的,第3個參數爲1,表示這個程序僅僅只畫了1個點。當程序調用gl.DrawArray()函數時,頂點着色器將被執行count次,每次處理一個頂點,這時候頂點着色器開始執行內部的main()函數,而後設置位置和尺寸;執行完成後,片元着色器開始執行其main()函數,設置顏色;最後,一個紅色的10個像素大的點就被繪製在了(0.0,0.0,0.0,1.0)處,也就是繪製區域的中心位置。
如今,咱們對頂點着色器和片元着色器的工做服方式有了大體的瞭解,只是有個問題,爲什麼(0.0,0.0,0.0,1.0)會繪製到了<canvas>的中心位置?下次咱們介紹座標系統會解開這個謎題。