WebGL Demo
https://mdn.github.io/dom-examples/webgl-examples/tutorial/sample5/index.html
現在讓我們給之前的正方形添加五個面從而可以創建一個三維的立方體。最簡單的方式就是通過調用方法?gl.drawElements()?使用頂點數組列表來替換之前的通過方法gl.drawArrays()?直接使用頂點數組。而頂點數組列表里保存著將會被引用到一個個獨立的頂點。
其實現在會存在這樣一個問題:每個面需要 4 個頂點,而每個頂點會被 3 個面共享。我們會創建一個包含 24 個頂點的數組列表,通過使用數組下標來索引頂點,然后把這些用于索引的下標傳遞給渲染程序而不是直接把整個頂點數據傳遞過去,這樣來減少數據傳遞。那么也許你就會問:那么使用 8 個頂點就好了,為什么要使用 24 個頂點呢?這是因為每個頂點雖然被 3 個面共享但是它在每個面上需要使用不同的顏色信息。24 個頂點中的每一個都會有獨立的顏色信息,這就會造成每個頂點位置都會有 3 份副本。
定義立方體頂點位置
首先,更新?initBuffers()
?函數中代碼來創建立方體的頂點位置緩存區。現在的代碼看起來和渲染正方形時的代碼很相似,只是比之前的代碼更長因為現在有了 24 個頂點(每個面使用 4 個頂點):
備注:?在“init-buffers.js”文件?initPositionBuffer()
?函數中,用下面代碼替換?positions
:
JSCopy to Clipboard
const positions = [// Front face-1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0,// Back face-1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0,// Top face-1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0,// Bottom face-1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0,// Right face1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0,// Left face-1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0,
];
由于我們給頂點添加了 Z 分量,因此我們需要將?vertexPosition
?屬性的?numComponents
?更新為 3。
備注:?在“draw-scene.js”文件?setPositionAttribute()
?函數中,將?numComponents
?從?2
?改為?3
:
JSCopy to Clipboard
const numComponents = 3;
定義頂點顏色
然后我們還要為每個頂點定義顏色。下面的代碼首先為每個面定義顏色,然后用一個循環語句為每個頂點定義顏色信息。
備注:?在“init-buffers.js”文件?initColorBuffer()
?函數中,用下面代碼替換?colors
?定義:
JSCopy to Clipboard
const faceColors = [[1.0, 1.0, 1.0, 1.0], // Front face: white[1.0, 0.0, 0.0, 1.0], // Back face: red[0.0, 1.0, 0.0, 1.0], // Top face: green[0.0, 0.0, 1.0, 1.0], // Bottom face: blue[1.0, 1.0, 0.0, 1.0], // Right face: yellow[1.0, 0.0, 1.0, 1.0], // Left face: purple
];// Convert the array of colors into a table for all the vertices.var colors = [];for (var j = 0; j < faceColors.length; ++j) {const c = faceColors[j];// Repeat each color four times for the four vertices of the facecolors = colors.concat(c, c, c, c);
}
定義元素(三角形)數組
既然已經創建好了頂點數組,接下來就要創建元素(三角形)數組了。
備注:?在“init-buffer.js”文件中添加下面的函數:
JSCopy to Clipboard
function initIndexBuffer(gl) {const indexBuffer = gl.createBuffer();gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);// This array defines each face as two triangles, using the// indices into the vertex array to specify each triangle's// position.const indices = [0,1,2,0,2,3, // front4,5,6,4,6,7, // back8,9,10,8,10,11, // top12,13,14,12,14,15, // bottom16,17,18,16,18,19, // right20,21,22,20,22,23, // left];// Now send the element array to GLgl.bufferData(gl.ELEMENT_ARRAY_BUFFER,new Uint16Array(indices),gl.STATIC_DRAW,);return indexBuffer;
}
indices
?數組聲明每一個面都使用兩個三角形來渲染。通過立方體頂點數組的索引指定每個三角形的頂點。那么這個立方體就是由 12 個三角形組成的了。
備注:?在“init-buffers.js”文件?initBuffers()
函數中,添加下面的代碼替換之前的?return
?代碼片段:
JSCopy to Clipboard
const indexBuffer = initIndexBuffer(gl);return {position: positionBuffer,color: colorBuffer,indices: indexBuffer,
};
渲染立方體
接下來就需要在?drawScene()
?函數里添加代碼使用立方體頂點索引數據來渲染這個立方體了。代碼里添加了對?gl.bindBuffer()?和?gl.drawElements()的調用:
備注:?在?drawScene()
?函數中,gl.useProgram
?代碼前添加如下代碼:
JSCopy to Clipboard
// Tell WebGL which indices to use to index the vertices
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffers.indices);
備注:?在“draw-scene.js”文件?drawScene()
?函數中,用下面這段代碼替換之前?gl.drawArrays()
:
JSCopy to Clipboard
{const vertexCount = 36;const type = gl.UNSIGNED_SHORT;const offset = 0;gl.drawElements(gl.TRIANGLES, vertexCount, type, offset);
}
立方體的每個面都由 2 個三角形組成,那就是每個面需要 6 個頂點,或者說總共 36 個頂點,盡管有許多重復的。然而,因為索引數組的每個元素都是簡單的整數類型,所以每一幀動畫需要傳遞給渲染程序的數據也不是很多。
最后,讓我們把變量?squareRotation
?替換成?cubeRotation
?并添加 X 軸的第二個旋轉。
備注:?在“webgl-demo.js”文件的頭部,把變量?squareRotation
?替換成?cubeRotation
:
JSCopy to Clipboard
let cubeRotation = 0.0;
備注:?在?drawScene()
?函數聲明中,將變量?squareRotation
?替換成?cubeRotation
:
JSCopy to Clipboard
function drawScene(gl, programInfo, buffers, cubeRotation) {
備注:?在?drawScene()
?函數中,用下面代碼替換之前的?mat4.rotate
?函數:
JSCopy to Clipboard
mat4.rotate(modelViewMatrix, // destination matrixmodelViewMatrix, // matrix to rotatecubeRotation, // amount to rotate in radians[0, 0, 1],
); // axis to rotate around (Z)
mat4.rotate(modelViewMatrix, // destination matrixmodelViewMatrix, // matrix to rotatecubeRotation * 0.7, // amount to rotate in radians[0, 1, 0],
); // axis to rotate around (Y)
mat4.rotate(modelViewMatrix, // destination matrixmodelViewMatrix, // matrix to rotatecubeRotation * 0.3, // amount to rotate in radians[1, 0, 0],
); // axis to rotate around (X)
備注:?在?main()
?函數中,替換?drawScene()
?函數調用參數中的?squareRotation
?為?cubeRotation
:
JSCopy to Clipboard
drawScene(gl, programInfo, buffers, cubeRotation);
cubeRotation += deltaTime;
到現在為止,我們已經創建了一個顏色生動的并且會在場景中移動和旋轉的立方體,這一定很酷吧。