到目前為止我們都只處理2D的成像,今天進入3D的世界。其實呢,如果我們把電腦螢幕,
當成觀測我們虛擬世界的一個窗口(跟xy平面平行,xy值都落在正負一之間),之前幾天畫的三角形影像,只是投影在其上而已,因為使用正交投影,
所以我們無法觀測到z值的改變會造成視覺上有什麼變化。
實際上我們輸入的vertex 3D位置,到最後螢幕上的平面坐標,是經過兩個矩陣的轉換: model-view矩陣以及p矩陣
先討論 model-view矩陣。這個矩陣代表你建立的model如何到你最後的view之間的坐標轉換,
而這中間其實是兩階段的轉換,model-world,world-view,
因為你建立的model,其實自己的局部坐標系。不一定跟世界坐標系重合(但也可以重合)
所以要從model的坐標系,轉換至世界坐標系。而世界坐標系如何投影至你的螢幕,就跟你的攝影機位置設定在哪有關了。
這就是world要投影至view的部分。
接下來是p矩陣。投影方式有正交投影即透視投影,簡單講,透視投影就是近的比較大,遠的比較小,比較符合我們看這世界的方式。
在透視投影中,所有的線都會交於螢幕後方的虛擬一個點。而正交投影(平行投影)則是所有的投影線都是平行的,使得物體不管遠近,
看起來都是一樣大。
所以我們的任務是計算出model-view矩陣,以及p矩陣,然後丟給vertex shader程式。若是在舊時代fixed functionality,有很多現成的函式庫可以使用
,例如glut,但是在WebGL時代,我們必須自己來,幸好已經有現成的矩陣函式庫了! http://glmatrix.net/
引入函式庫之後,我們就來畫一個三角柱。三角柱的結構如底下所示:
上圖出自Brian Danchilla所著Beginning WebGL for HTML5第29頁
我們可以看到這結構是由12個點組成。我們這次採用新的方法,我們只上傳點的資料,
但是這些點怎麼繪圖成面以另外的陣列儲存。
首先先修改vertex shader部分
attribute vec3 aVertexPosition;
attribute vec3 aVertexColor;
varying highp vec4 vColor;
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
void main(void) {
gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
vColor = vec4(aVertexColor, 1.0);
}
加入uMVMatrix跟pMatrix兩個uniform變數,再將vertex position乘上這兩個矩陣!為什麼設為uniform是因為那是不會變動的!
之後在webGL中創建兩個矩陣
var
mvMatrix = mat4.create(),
pMatrix = mat4.create();
設立兩個函式,將這兩個矩陣與shader連結
function getMatrixUniforms(){
glProgram.pMatrixUniform = gl.getUniformLocation(glProgram, "uPMatrix");
glProgram.mvMatrixUniform = gl.getUniformLocation(glProgram, "uMVMatrix");
}
function setMatrixUniforms() {
gl.uniformMatrix4fv(glProgram.pMatrixUniform, false, pMatrix);
gl.uniformMatrix4fv(glProgram.mvMatrixUniform, false, mvMatrix);
}
再將init函式中加入這兩個函式,將之前的setupdynamicbuffer刪掉
function init() {
canvas = document.getElementById("main_canvas");
gl = initWebGL(canvas);
if (gl) {
initShaders();
setupBuffers();
getMatrixUniforms();
(function animLoop(){
setupWebGL();
//setupDynamicBuffers();
setMatrixUniforms();
drawScene();
requestAnimationFrame(animLoop, canvas);
})();
}
}
在setupWebGL 函式加入設定矩陣程式
mat4.perspective(45, canvas.width / canvas.height, 0.1, 100.0, pMatrix);
mat4.identity(mvMatrix);
mat4.translate(mvMatrix, [-1.0, -1.0, -7.0]);
mat4.rotate(mvMatrix, angle, [0.0, 1.0, 0.0]);
angle += 0.01;
如此一來我們就設定好mv矩陣與p矩陣了!
mv矩陣會隨著時間對y軸旋轉。
接下來設定我們的模型,
在setupBuffers函式重新加入質點位置與質點顏色,這邊質點的坐標是保持不動的,我們設計動的是坐標系會旋轉!
var triangleVerticeColors = [
//front face
0.0, 0.0, 1.0,
1.0, 1.0, 1.0,
0.0, 0.0, 1.0,
0.0, 0.0, 1.0,
0.0, 0.0, 1.0,
1.0, 1.0, 1.0,
//rear face
0.0, 1.0, 1.0,
1.0, 1.0, 1.0,
0.0, 1.0, 1.0,
0.0, 1.0, 1.0,
0.0, 1.0, 1.0,
1.0, 1.0, 1.0
];
trianglesColorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, trianglesColorBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(triangleVerticeColors), gl.STATIC_DRAW);
var triangleVertices = [
//front face
//bottom left to right, to top
0.0, 0.0, 0.0,
1.0, 0.0, 0.0,
2.0, 0.0, 0.0,
0.5, 1.0, 0.0,
1.5, 1.0, 0.0,
1.0, 2.0, 0.0,
//rear face
0.0, 0.0, -2.0,
1.0, 0.0, -2.0,
2.0, 0.0, -2.0,
0.5, 1.0, -2.0,
1.5, 1.0, -2.0,
1.0, 2.0, -2.0,
];
trianglesVerticeBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, trianglesVerticeBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(triangleVertices), gl.STATIC_DRAW);
我們再設定好這些直點所組成的面的buffer!
//setup vertice buffers
//18 triangles
var triangleVertexIndices = [
//front face
0,1,3,
1,3,4,
1,2,4,
3,4,5,
//rear face
6,7,9,
7,9,10,
7,8,10,
9,10,11,
//left side
0,3,6,
3,6,9,
3,5,9,
5,9,11,
//right side
2,4,8,
4,8,10,
4,5,10,
5,10,11,
//bottom faces
0,6,8,
8,2,0
];
triangleVerticesIndexBuffer = gl.createBuffer();
triangleVerticesIndexBuffer.number_vertex_points = triangleVertexIndices.length;
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, triangleVerticesIndexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(triangleVertexIndices), gl.STATIC_DRAW);
這邊的indice就是說組成三角形是我們vertex buffer中的哪三個索引的點,注意bindBuffer跟bufferData使用的參數是gl.ELEMENT_ARRAY_BUFFER
最後在drawScene中
drayArray部分改成drawElement
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, triangleVerticesIndexBuffer);
gl.drawElements(gl.TRIANGLES, triangleVerticesIndexBuffer.number_vertex_points, gl.UNSIGNED_SHORT, 0);
最後記得再 setupWebGL函式 enable depth test ,才看得出來深度的差別
gl.enable(gl.DEPTH_TEST);
gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT);
這樣就完成了!
結果就是一個一直旋轉的三角柱