四、繪制一個點


  上次我們介紹了如何在<canvas>中使用WebGL,以及幾個基礎的WebGL函數;實現了背景色的重置;為了擴展方便,我們把上次的代碼做了些改動,將繪制圖形的js獨立成文件,這樣我們只關注與這個js文件的編寫;以后除非HTML文件發生變化,我們就跳過它,直接討論JavaScript代碼。

 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>
Hello point.html

  接下來,我們在此基礎上,繪制一個位於原點(0.0,0.0,0.0)處的10個像素大的紅色的點。因為使用的是三維圖形上下文,所以指定這個點時需要使用三維坐標。坐標系統后面介紹,這里只需要理解為原點位於<canvas>中心位置。效果如如下:

 

  實際上,我們使用矩形而不是圓來繪制一個點,因為繪制矩形比繪制圓更快;就像在前一次中我們以RGBA的形式指定了背景色一樣,這里也需要同樣的處理;在前面,我們使用2d上下文來繪制了一個矩形;先指定了繪圖顏色,然后進行繪制。你可能認為WebGL也差不多,不幸的是,沒那么簡單。WebGL依賴於一種新的稱為着色器的繪圖機制。着色器提供了靈活且強大的繪制二維或三維圖形的方法,所有WebGL必須使用它。正因為強大,所以更復雜。

  要使用WebGL繪圖,必須使用着色器,哪怕是一個點(矩形);着色器程序是以字符串的形式“嵌入”在JavaScript文件中,並且在程序開始運行前就已經設置好了。WebGL需要使用兩種着色器:頂點着色器、片源着色器;下面分別介紹:

  頂點着色器:頂點着色器是用來描述頂點特性(如位置、顏色等)的程序。頂點是指二維或三維空間中的一個點,比如二維或三維圖形的端點或交點。

  片元着色器:進行逐片元處理過程(如光源)的程序。片元是一個WebGL術語,你可以將其理解為像素(圖像的單元)。

  在后續,我們會詳細的學習着色器。簡單的說,在三維場景中,僅僅用線條和顏色把圖形畫出來是遠遠不夠的。你必須考慮如光線照上去后,或者觀察者的視角發生變化時,對場景會有什么影響。着色器可以高度靈活的完成這些工作;提供各種渲染效果。這也就是現在制作的三維場景如此逼真的原因。

  

  上圖顯示了WebGL系統的執行流程;左側為瀏覽器,首先執行JavaScript程序,調用了WebGL的相關方法,然后頂點着色器和片元着色器就會執行,頂點着色器指定繪制圖形的位置和尺寸;片元着色器則指定繪制圖形的顏色;然后在顏色緩沖區內進行繪制,這時就清空了繪圖區,最后,顏色緩沖區中的內容就自動在瀏覽器的<canvas>標簽上顯示出來。

  回到我們今天的目標來,下面顯示了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 }
hello-point.js

 

  這個文件包含三個部分,頂點着色器程序(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>的中心位置?下次我們介紹坐標系統會解開這個謎題。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM