iT邦幫忙

DAY 13
0

30 天實戰跨平台行動APP系列 第 13

Day 13 WebGL 3D

到目前為止我們都只處理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); 	

這樣就完成了!

結果就是一個一直旋轉的三角柱


上一篇
Day 12 WebGL 移動
下一篇
Day 14 WebGL 星空與地球
系列文
30 天實戰跨平台行動APP26
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言