目錄html
不得不說如今三維圖形渲染技術更新換代實在是太快,OpenGL不少資料還沒來得及學習就已經有點落伍了。NeHe的學習教程還有以前用的《OpenGL編程指南》第七版(也就是紅寶書)都很是好,惋惜它們都是從固定管線開始講起的;而如今可編程管線的技術已是很是常見的基礎技術了。後來我還看過《OpenGL編程指南》第八版(白皮書),這本教程是從可編程管線(着色器)開始講起的,看的時候就以爲沒有前面的基礎打底,顯得很是的晦澀,遠不如紅寶書易懂。羞愧的說,我已經屢次入門失敗了。前端
這也正是我寫這篇教程的緣由,但願從繁雜的資料中總結真正有用的知識(固然也但願能幫助到你)。我以爲WebGL是學習OpenGL系列三維圖形渲染技術很好的入門點。WebGL是OpenGL的瀏覽器版本,基本上能夠認爲是OpenGL的子集,能被WebGL保留而不剔除的技術,必須是三維圖形渲染技術的精華。在這裏給你們強烈推薦《WebGL編程指南》這本書,我這篇教程正是在這本書的基礎之上總結出來的。web
在學習OpenGL/WebGL的時候,我還感受到不少資料舉得例子每每都太簡單了,確實是一看就懂,可是在實際遇到的問題的時候卻每每解決不了。我仍是認爲在實際中解決問題,更能加深對知識的理解。正好最近我在研究GIS中地形的繪製,那麼我就經過一步一步繪製地形的示例,來總結WebGL的相關知識。若是你不懂GIS這些術語也沒關係,只須要知道我這裏的最終目的是想繪製的是一個大地高程模型,是一個包含XYZ座標的點集,表達了地形的狀況。編程
編寫WebGL程序跟編寫Web前端程序的步驟是同樣的,包含HTML和JavaScript兩個部分,經過瀏覽器進行調試。canvas
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>Draw a point (1)</title> </head> <body onload="main()"> <canvas id="webgl" width="400" height="400"> Please use a browser that supports "canvas" </canvas> <script src="../lib/webgl-utils.js"></script> <script src="../lib/webgl-debug.js"></script> <script src="../lib/cuon-utils.js"></script> <script src="HelloPoint1.js"></script> </body> </html>
這一段HTML很是簡單,從實際表現上來講就是建立了一個畫布<canvas>。<canvas>是HTML5引入的的一個繪製標籤,能夠在畫布中繪製任意圖形。WebGL正是經過<canvas>元素進行繪製的。
除此以外,這段代碼還經過<script>標籤引入了幾個外部JS文件。其中lib目錄中的幾個JS文件是一些通用的組件(來自《WebGL編程指南》的源碼),能夠先暫時不用關心其具體實現;最後一個導入的HelloPoint1.js正是咱們編寫的繪製模塊。而在<body>標籤中定義的onload事件屬性綁定的正是HelloPoint1.js中的main()函數。瀏覽器
// 頂點着色器程序 var VSHADER_SOURCE = 'void main() {\n' + ' gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n' + // Set the vertex coordinates of the point ' gl_PointSize = 10.0;\n' + // Set the point size '}\n'; // 片元着色器程序 var FSHADER_SOURCE = 'void main() {\n' + ' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' + // Set the point color '}\n'; function main() { // 獲取 <canvas> 元素 var canvas = document.getElementById('webgl'); // 獲取WebGL渲染上下文 var gl = getWebGLContext(canvas); if (!gl) { console.log('Failed to get the rendering context for WebGL'); return; } // 初始化着色器 if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) { console.log('Failed to intialize shaders.'); return; } // 指定清空<canvas>的顏色 gl.clearColor(0.0, 0.0, 0.0, 1.0); // 清空<canvas> gl.clear(gl.COLOR_BUFFER_BIT); // 繪製一個點 gl.drawArrays(gl.POINTS, 0, 1); }
這段JS代碼的主要內容就是前面提到的main函數,一旦HTML被瀏覽器加載成功,這段腳本就會執行。在main函數中主要有一下幾步:函數
document.getElementById('webgl'):文檔對象模型DOM的函數,獲取到HTML頁面的<canvas>元素。學習
getWebGLContext(canvas):獲取WebGL渲染上下文,保存在gl變量中。由於不一樣瀏覽器獲取函數不太同樣,因此經過組件cuon-utils提供的函數來統一行爲。webgl
initShaders:初始化着色器。debug
首先要知道什麼是着色器。若是你只學習過固定管線或者其餘的二維繪圖組件(如GDI),就會很是困惑着色器是什麼,爲何要用着色器。好比說在固定管線中,繪製點就是drawPoint,繪製線就drawLine。而在WebGL中,繪製工做則主要被分解成頂點着色器和片元着色器兩個步驟了。
在啓動JS程序後,繪製工做首先進入的是頂點着色器,在頂點着色器中描述頂點特性(如位置、顏色等),頂點就是三維空間的點,好比三角形的三個頂點;而後進入到片元着色器,在片元着色器中逐片元處理像素(如光照、陰影、遮擋)。最後片元傳入到顏色緩衝區,進行顯示。渲染過程以下:
這個過程是一個相似水流的流向過程,因此這個過程被稱爲渲染管線(Pipeline)。而且,這個過程是須要咱們去編程控制的,好比觀察者的視角變化須要在頂點着色器去調控;光線對顏色的變化須要在片元着色器去調控等;所以,這個過程就是可編程管線。經過着色器程序,三維圖像渲染就更加的靈活強大。
在initShaders()函數中,傳入了預先定義的JS字符串VSHADER_SOURCE和FSHADER_SOURCE。須要說明是,着色器程序是以字符串的形式嵌入到JS文件中運行的。這個函數一樣是cuon-utils組件提供的,調用以後就告訴WebGL系統着色器已經創建好了並能夠隨時使用。
頂點着色器的定義以下:
// 頂點着色器程序 var VSHADER_SOURCE = 'void main() {\n' + ' gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n' + // Set the vertex coordinates of the point ' gl_PointSize = 10.0;\n' + // Set the point size '}\n';
前面說到頂點着色器程序是嵌入在JS中的程序,因此雖然傳入的是字符串,但其實本質是着色器描述語言(GLSL:OpenGL Shading Language)。既然是語言也就有本身的函數與變量定義。main()函數是每一個着色器程序定義的入口。在main函數中,將頂點的座標賦值給內置變量gl_Position,點的尺寸賦值給內置變量gl_PointSize。
注意這裏的gl_Position是必須賦值的,不然着色器不會正常工做。賦值的類型是vec4,也就是一個四維矢量。通常來講,描述點位只須要三維矢量就能夠了,可是不少狀況下須要四個份量的齊次座標。齊次座標(x,y,z,w)等價於三維座標(x/w,y/w,z/w)。因此若是第四個份量是1,那麼就是普通的三維座標;若是第四份量爲0,就表示無窮遠的點。
片元着色器的定義以下:
// 片元着色器程序 var FSHADER_SOURCE = 'void main() {\n' + ' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' + // Set the point color '}\n';
如同頂點着色器同樣,片元着色器將點的顏色賦值給gl_FragColor變量,gl_FragColor是片元着色器惟一的內置變量,控制像素在屏幕上的最終顏色。
gl.clearColor():設置清空的背景色。
gl.clear(gl.COLOR_BUFFER_BIT): 清空顏色緩衝區。
gl.drawArrays(gl.POINTS, 0, 1):繪製一個點。
頂點着色器只是指定了繪製的頂點,還須要指定頂點到底成點、成線仍是成面,gl.drawArrays()就是這樣一個函數,這裏告訴WebGL系統應該繪製一個點。
最終的運行結果很簡單,在Chrome打開HelloPoint1.html,頁面顯示了一個繪製一個點的窗口:
原本部分代碼和插圖來自《WebGL編程指南》,源代碼連接:https://share.weiyun.com/5VjlUKo ,密碼:sw0x2x。會在此共享目錄中持續更新後續的內容。