WebGL開啟了網頁3D渲染的新時代,它允許在canvas中直接渲染3D的內容,而不借助任何插件。WebGL同canvas 2D的API一樣,都是通過腳本操縱對象,所以步驟也是基本相似:准備工作上下文,准備數據,在canvas中繪制對象並渲染。與2D不同的就是3D涉及的知識更多了,例如世界、光線、紋理、相機、矩陣等專業知識。WebGL有一個很好的中文教程,就是下面使用參考中的第一個鏈接,所以這裡不再班門弄斧,後面的內容只是簡單的總結一下學習的內容。
浏覽器的支持
由於微軟有自己的圖形發展計劃,一直不支持WebGL,所以IE目前除了安裝插件外,是無法運行WebGL的。其他的主流浏覽器如Chrome、Firefox、Safari、Opera等,都裝上最新的版本就可以了。除了浏覽器要裝最新的外,還要保證顯卡的驅動也是最新的。
裝上這些以後,可以打開浏覽器,輸入下面的網址驗證一下浏覽器對WebGL的支持情況:http://webglreport.sourceforge.Net/。
在正常安裝以上浏覽器之後還是不能運行WebGL,那你可以強制開啟WebGL支持試一試。開啟方法如下:
Chrome浏覽器
我們需要為Chrome加入一些啟動參數,以下具體操作步驟以Windows操作系統為例:找到Chrome浏覽器的快捷方式,右鍵點擊快捷方式,選擇屬性;在目標框內,Chrome.exe後面的引號後面,加入以下內容:
--enable-webgl--ignore-gpu-blacklist--allow-file-Access-from-files
點擊確定後關閉Chrome,然後用此快捷方式啟動Chrome浏覽器。
幾個參數的含義如下:
--enable-webgl的意思是開啟WebGL支持;
--ignore-gpu-blacklist的意思是忽略GPU黑名單,也就是說有一些顯卡GPU因為過於陳舊等原因,不建議運行WebGL,這個參數可以讓浏覽器忽略這個黑名單,強制運行WebGL;
--allow-file-Access-from-files的意思是允許從本地載入資源,如果你不是WebGL的開發者,不需要開發調試WebGL,只是想要看一下WebGL的Demo,那你可以不添加這個參數。
Firefox浏覽器
Firefox的用戶請在浏覽器的地址欄輸入“about:config”,回車,然後在過濾器(filter)中搜索“webgl”,將webgl.force-enabled設置為true;將webgl.disabled設置為false;在過濾器(filter)中搜索“security.fileuri.strict_origin_policy”,將security.fileuri.strict_origin_policy設置為false;然後關閉目前開啟的所有Firefox窗口,重新啟動Firefox。
前兩個設置是強制開啟WebGL支持,最後一個security.fileuri.strict_origin_policy的設置是允許從本地載入資源,如果你不是WebGL的開發者,不需要開發調試WebGL,只是想要看一下WebGL的Demo,那你可以不設置此項。
Safari浏覽器
在菜單中找到“屬性”→“高級”,選中“顯示開發菜單”,然後到“開發”菜單,選中“開啟WebGL”。
開發步驟
下面的代碼只是簡單總結一下相關的概念,它來源於參考中的中文教程,涉及較多的3D方面的知識。感興趣的同學直接可以跳到實用參考中的中文教程中學習,比我這裡講解的要詳細和准確的多。湊熱鬧的同學簡單看看就可以了,不用深究每一行代碼的含義。
准備工作
這個不用說了,就是在頁面上添加一個canvas元素作為渲染的容器。例如:
復制代碼代碼如下:
www.mb5u.com
<bodyonload="start()">
<canvasid="glcanvas"width="640"height="480">
Yourbrowserdoesn'tappeartosupporttheHtml5canvaselement.
</canvas>
</body>
下面就是正式開始寫腳本的時候了,首先看一下程序入口以及整體結構:
復制代碼代碼如下:
www.mb5u.com
functionstart(){
varcanvas=document.getElementById("glcanvas");
initGL(canvas);
initShaders();
initBuffers();
gl.clearColor(0.0,0.0,0.0,1.0);
gl.enable(gl.DEPTH_TEST);
drawScene();
}
這裡的幾個方法代表了典型的WebGL的繪制步驟:
步驟一:初始化WebGL工作環境 - initGL
這個方法的代碼如下:
復制代碼代碼如下:
www.mb5u.com
vargl;
functioninitGL(canvas){
gl=null;
try{
//Trytograbthestandardcontext.Ifitfails,fallbacktoexperimental.
gl=canvas.getContext("webgl")||canvas.getContext("experimental-webgl");
}
catch(e){} //Ifwedon'thaveaGLcontext,giveupnow
if(!gl){
alert("UnabletoinitializeWebGL.Yourbrowsermaynotsupportit.");
}
}
這個方法很簡單,就是獲取WebGL的繪制環境,需要把參數"webgl"傳給canvas.getContext方法就行了,但是由於目前WebGL的標准沒有最終定型,所以實驗階段用的參數都是"experimental-webgl"。當然你直接去調用canvas.getContext("experimental-webgl")也是可以的,等標准定下以後,你再修改一個代碼。
步驟二:初始化著色器Shaders - initShaders
著色器Shader概念比較簡單,說白了就是顯卡運算指令。構造3D場景需要進行大量的顏色、位置等等信息的計算,如果這些計算由軟件執行的話,速度會很慢。所以把這些運算讓顯卡去計算,速度就很快;如何去執行這些計算,就是由著色器指定的。著色器代碼是用一種叫做GLSL的著色器語言編寫的,這個我們不去講述這個語言了。
著色器可以在Html中定義,在代碼中使用。當然了你在程序中用一個字符串去定義著色器也是一樣的。
下面先看定義的部分:
復制代碼代碼如下:
www.mb5u.com
<scriptid="shader-fs"type="x-shader/x-fragment">
precisionmediumpfloat;
varyingvec4vColor;
voidmain(void){
gl_FragColor=vColor;
}
</script>
<scriptid="shader-vs"type="x-shader/x-vertex">
attributevec3aVertexPosition;
attributevec4aVertexColor;
uniformmat4uMVMatrix;
uniformmat4uPMatrix;
varyingvec4vColor;
voidmain(void){
gl_Position=uPMatrix*uMVMatrix*vec4(aVertexPosition,1.0);
vColor=aVertexColor;
}
</script>
這裡有兩個著色器:面著色器和頂點著色器。
關於這兩個著色器,這裡有必要說明一下,計算機中的3D模型基本都是由點結合三角面片去描述的,頂點著色器就是去處理這些點的數據,而面著色器就是通過插值的方式,去處理三角面片上點的數據。
上面定義的頂點著色器就定義了頂點的位置和顏色計算方式;而面著色器定義了插值點的顏色計算方式。實際的應用場景中,還會涉及到在著色器中處理光線等效果。
定義了著色器,在程序中就可以查找到它們並可以去使用:
復制代碼代碼如下:
www.mb5u.com
varshaderProgram;
functioninitShaders(){
varfragmentShader=getShader(gl,"shader-fs");
varvertexShader=getShader(gl,"shader-vs");
shaderProgram=gl.createProgram();
gl.attachShader(shaderProgram,vertexShader);
gl.attachShader(shaderProgram,fragmentShader);
gl.linkProgram(shaderProgram);
if(!gl.getProgramParameter(shaderProgram,gl.LINK_STATUS)){
alert("Couldnotinitialiseshaders");
}
gl.useProgram(shaderProgram);
shaderProgram.vertexPositionAttribute=gl.getAttribLocation(shaderProgram,"aVertexPosition");
gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);
shaderProgram.vertexColorAttribute=gl.getAttribLocation(shaderProgram,"aVertexColor");
gl.enableVertexAttribArray(shaderProgram.vertexColorAttribute);
shaderProgram.pMatrixUniform=gl.getUniformLocation(shaderProgram,"uPMatrix");
shaderProgram.mvMatrixUniform=gl.getUniformLocation(shaderProgram,"uMVMatrix");
}
著色器是有了,但是怎麼讓顯卡去執行,Program就是這種橋梁,它是WebGL原生的二進制碼,它的作用基本上就是讓顯卡運行著色器代碼去渲染指定的模型數據。
這裡還用到一個輔助方法getShader,這個方法就是遍歷Html文檔,查找著色器的定義,拿到定義後創建著色器,這裡就不細說了:
復制代碼代碼如下:
www.mb5u.com
functiongetShader(gl,id){
varshaderScript,theSource,currentChild,shader;
shaderScript=document.getElementById(id);
if(!shaderScript){
returnnull;
}
theSource="";
currentChild=shaderScript.firstChild;
while(currentChild){
if(currentChild.nodeType==currentChild.TEXT_NODE){
theSource+=currentChild.textContent;
}
currentChild=currentChild.nextSibling;
}
if(shaderScript.type=="x-shader/x-fragment"){
shader=gl.createShader(gl.FRAGMENT_SHADER);
}elseif(shaderScript.type=="x-shader/x-vertex"){
shader=gl.createShader(gl.VERTEX_SHADER);
}else{
//Unknownshadertype
returnnull;
}
gl.shaderSource(shader,theSource);
//Compiletheshaderprogram
gl.compileShader(shader);
//Seeifitcompiledsuccessfully
if(!gl.getShaderParameter(shader,gl.COMPILE_STATUS)){
alert("Anerroroccurredcompilingtheshaders:"+gl.getShaderInfoLog(shader));
returnnull;
}
returnshader;
}
步驟三:創建/加載模型數據 - initBuffers
這些小例子中,模型數據基本都是直接生成的,實際的程序中,這些數據應該都是從模型加載得到的:
復制代碼代碼如下:
www.mb5u.com
vartriangleVertexPositionBuffer;
vartriangleVertexColorBuffer;
functioninitBuffers(){
triangleVertexPositionBuffer=gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER,triangleVertexPositionBuffer);
varvertices=[
0.0,1.0,0.0,
-1.0,-1.0,0.0,
1.0,-1.0,0.0
];
gl.bufferData(gl.ARRAY_BUFFER,newFloat32Array(vertices),gl.STATIC_DRAW);
triangleVertexPositionBuffer.itemSize=3;
triangleVertexPositionBuffer.numItems=3;
triangleVertexColorBuffer=gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER,triangleVertexColorBuffer);
varcolors=[
1.0,0.0,0.0,1.0,
0.0,1.0,0.0,1.0,
0.0,0.0,1.0,1.0
];
gl.bufferData(gl.ARRAY_BUFFER,newFloat32Array(colors),gl.STATIC_DRAW);
triangleVertexColorBuffer.itemSize=4;
triangleVertexColorBuffer.numItems=3;
}
上面這段代碼創建了三角形的頂點和頂點的顏色數據並放在緩沖區中。
步驟四:渲染 - drawScene
准備好了數據以後,交給WebGL去渲染就好了,這裡調用的是gl.drawArrays方法。看代碼:
復制代碼代碼如下:
www.mb5u.com
functiondrawScene(){
gl.viewport(0,0,gl.viewportWidth,gl.vIEwportHeight);
gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT);
pMatrix=okMat4Proj(45.0,gl.viewportWidth/gl.vIEwportHeight,0.1,100.0);
mvMatrix=okMat4Trans(-1.5,0.0,-7.0);
gl.bindBuffer(gl.ARRAY_BUFFER,triangleVertexPositionBuffer);
gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute,triangleVertexPositionBuffer.itemSize,gl.FLOAT,false,0,0);
gl.bindBuffer(gl.ARRAY_BUFFER,triangleVertexColorBuffer);
gl.vertexAttribPointer(shaderProgram.vertexColorAttribute,triangleVertexColorBuffer.itemSize,gl.FLOAT,false,0,0);
setMatrixUniforms();
gl.drawArrays(gl.TRIANGLES,0,triangleVertexPositionBuffer.numItems);
}
這個函數首先設置了3D世界的背景為黑色,然後設置投影矩陣,設置待繪制對象的位置,然後根據緩沖中的頂點和顏色數據,繪制對象。這裡還有一些生成投影矩陣和模型視圖矩形的輔助方法(使用了Oak3D圖形庫中的矩陣輔助方法)與主題關系不大,這裡就不詳細解釋了。
基本上流程就是這麼多了,更復雜的紋理,光線等都是在這些基礎上加入一些WegGL的特性實現的,這個請參看後面的中文教程,裡面有詳細的例子。
怎麼樣?使用原生的WebGL開發是一種什麼感受?不僅需要有深厚的3D知識,還需要知道各種實現細節。WebGL這樣做是為了靈活的適應各種應用場景,但是對於大多數像我這樣非專業人士來說,很多細節是不需要知道的。這樣就催生了各種輔助開發的類庫,例如這節用到的Oak3D庫(為了演示WebGL開發,例子中只用到了矩陣輔助方法)。下一節會介紹一個用的比較多的Three.JS圖形庫。
實用參考:
中文教程:http://www.hiwebgl.com/?p=42
開發中心:https://developer.mozilla.org/en/WebGL