iT邦幫忙

2017 iT 邦幫忙鐵人賽
DAY 7
0
Modern Web

WebGL 與 Three.js 初探系列 第 7

[Day7] webGL修羅道(4) - 3D 與動畫

[Day7] webGL修羅道 - 3D 與動畫

3D 圖形基礎

在電腦上看到圖形的方式,通常是透過 camera 的概念。可以想像人的眼睛(或螢幕)就是一台相機的鏡頭,我們透過調整鏡頭的遠近跟位置來成像。而成像的方式通常會透過矩陣來轉換,為了方便說明,以下只列舉幾個實務上較常使用到的矩陣。

為了方便計算矩陣,我們使用到 gl-matrix.js 這個函式庫來簡化我們的矩陣計算。

一個正方體裡頭會有 8 個點。所以我們修改 createPoints 這個函數為 8 個頂點,

var vertices = [
    -1, -1, -1,     1, 0, 0, 1,     // 0
     1, -1, -1,     1, 1, 0, 1,     // 1
    -1,  1, -1,     0, 1, 1, 1,     // 2
     1,  1, -1,     0, 0, 1, 1,     // 3
    -1,  1,  1,     1, 0.5, 0, 1,   // 4
     1,  1,  1,     0.5, 1, 1, 1,   // 5
    -1, -1,  1,     1, 0, 0.5, 1,   // 6
     1, -1,  1,     0.5, 0, 1, 1,   // 7
  ];

前三個點代表座標,後面四個點則是代表顏色。我們要做的事情是將這 8 個點送給 GPU 繪製,同時也將顏色的資訊傳給 webGL。

用 gl.drawElements 取代 gl.drawArrays 畫出

如果只是單純修改頂點,你會發現畫出來的正方體只有三個邊。因為gl.TRANGLE_STRIP 的關係,所以我們每次的繪製,都會利用到前面兩個點,所以有兩個點沒有被繪製到。如果想要達成正方體的效果,我們必須要再加入兩個重複的點座標。

這顯然有點不合理。所以這邊我們使用 gl.drawElements 來取代 gl.drawArrays。drawElements 的不同點在於,你可以自己選擇要繪製的頂點數量和開始位置(index)。

所以接下來要做的事情就是建立一個繪製頂點的 index array,讓 webGL 如何重複使用這些頂點位置。

function createIndices() {
  // 建立索引,讓 webGL 知道要取用哪些頂點
  var indices = [
    0, 1, 2,   1, 2, 3,
    2, 3, 4,   3, 4, 5,
    4, 5, 6,   5, 6, 7,
    6, 7, 0,   7, 0, 1,
    0, 2, 6,   2, 6, 4,
    1, 3, 7,   3, 7, 5
  ];
  indexCount = indices.length;
  
  var indexBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint8Array(indices), gl.STATIC_DRAW);
}

同時,我們也修改了 createVertices 裡頭的傳值方式

// 原本為 gl.vertexAttribPointer(coord, 3, gl.FLOAT, false, 0, 0);
// 第五個參數代表每次取用頂點後,下一次的頂點從哪裡開始 Float32Array.BYTES_PER_ELEMENT * 7
gl.vertexAttribPointer(coords, 3, gl.FLOAT, false, Float32Array.BYTES_PER_ELEMENT * 7, 0);
gl.enableVertexAttribArray(coords);

建立動畫

通常我們在使用 3D 動畫效果時,我們會採用矩陣轉換的方式。我不想太深入數學理論,以下是動畫中常用的矩陣:

縮放:其中 Sx, Sy, Sz 代表縮放係數
http://ithelp.ithome.com.tw/upload/images/20161221/20103565TX2d6QMajC.png
旋轉:對 x, y, z 做 cos sin 運算,舉例:(對 x, y)坐旋轉。以此類推
http://ithelp.ithome.com.tw/upload/images/20161221/20103565KVxvq805tU.png
在 javascript 當中並沒有矩陣運算的功能,只能用陣列的方式來做到,所以這邊為了簡化大量的運算,我們引入了 gl-matrix.js 當作我們的範例,在 html 加入

<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.3.2/gl-matrix-min.js"></script>

以上是較粗淺的矩陣轉換介紹,之後的範例當中如果有機會再和各位詳細說明。

將 perspective matrix 及 transform matrix 加入 shader

uniform mat4 u_transformMatrix;
uniform mat4 u_perspectiveMatrix;

void main(void) {
    gl_Position = perspectiveMatrix * transformMatrix  * coords;
    gl_PointSize = pointSize;
    varyingColors = colors;
}

在 vertex shader 當中,我們加入兩個變數 transformMatrix 及 perspectiveMatrix,讓 javascript 傳值進入。一般矩陣的相乘在 GSGL 中直接用 * 表示即可。

在 draw function 中加入:

mat4.rotateX(matrix, matrix, -0.043); // 每次旋轉 X -0.043
mat4.rotateY(matrix, matrix, 0.01); // 每次旋轉 0.01
mat4.rotateZ(matrix, matrix, 0.01); // 每次旋轉 0.01

var transformMatrix = gl.getUniformLocation(shaderProgram, "transformMatrix");
gl.uniformMatrix4fv(transformMatrix, false, matrix);

還是要提醒一下,在 webGL 的座標系統當中,只有 -1 ~ 1

最後,就可以看到成果啦!

webGL cube animation

結論

到目前為止,我們已經學習了不少 webGL 的 API 跟運作方式,明天會介紹 webGL 中蠻重要的功能 - sampler(採樣器),並且將我們目前所學的知識做一些應用來當作 webGL 的結尾。之後就可以進入高階一點的世界 - Three.js 了。


上一篇
[Day6] three.js 前置 webGL 修羅道(3) - 動畫與 varying
下一篇
[Day8] webGL 修羅道 - 3D 紋理貼圖
系列文
WebGL 與 Three.js 初探30

尚未有邦友留言

立即登入留言