立方体 Cube :先找顶点索引 gl.ELEMENT_ARRAY_BUFFER

var verticsSize = new Float32Array([ -0.5, 0.5, 1.0, 0.0, 0.0, 1.0, -0.5, -0.5, 0.0, 1.0, 0.0, 1.0, 0.5, 0.5, 0.0, 0.0, 1.0, 1.0, 0.5, -0.5, 1.0, 0.0, 0.0, 1.0 ]); gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

gl.drawArrays() 中,可通过上述代码,用4个点来绘制两个三角形,从而画出一个正方形。 而一个立方体有6个面,所以要组织 4×6 = 24 个点的数据,才能画出一个立方体。而立方体实际上只有8个顶点。这里采用一种新的方法: gl.drawElements( mode, count, type, offset ) 来实现,它要 gl.createBuffer() 两次, 第1个 Buffer 装顶点索引 gl.ELEMENT_ARRAY_BUFFER;第2个 Buffer 装顶点位置和颜色 gl.ARRAY_BUFFER。 它可以避免重复定义顶点,保持顶点数量最小,重复部分用索引来替代。

用以下代码可画出上面的正方形。

<script id="vertexShader" type="x-shader/x-vertex"> attribute vec4 a_Position; attribute vec4 a_Color; uniform mat4 u_mvpMatrix; varying vec4 v_Color; void main(){ gl_Position = u_mvpMatrix * a_Position; v_Color = a_Color; } </script> <script id="fragmentShader" type="x-shader/x-fragment"> precision mediump float; varying vec4 v_Color; void main(){ gl_FragColor = v_Color; } </script> function init(){ var canvas = document.getElementById('canvas'), gl = canvas.getContext('webgl'); if(!gl){ console.log('Failed to get WebGL.'); return; } if(!initShaders(gl, "vertexShader", "fragmentShader")){ console.log('Failed to initShaders.'); return; } gl.clearColor( 0.0, 0.0, 0.0, 1.0 ); gl.enable( gl.DEPTH_TEST ); var viewProjMatrix = new Matrix4(); viewProjMatrix.setPerspective(45, canvas.width/canvas.height, 1, 100); viewProjMatrix.lookAt(2, 2, 7, 0, 0, 0, 0, 1, 0); var u_mvpMatrix = gl.getUniformLocation(gl.program, 'u_mvpMatrix'); gl.uniformMatrix4fv(u_mvpMatrix, false, viewProjMatrix.elements); var n = initVertexBuffers(gl); // 立方体 gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT ); gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0); } // v6----- v5 // /| /| // v1------v0| // | | | | // | |v7---|-|v4 // |/ |/ // v2------v3 function initVertexBuffers(gl){ // 顶点索引 var indices = new Uint8Array([ 0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5, 4, 7, 5, 5, 7, 6, 6, 7, 2, 6, 2, 1, 0, 1, 6, 0, 6, 5, 3, 2, 7, 3, 7, 4 ]); var indicesBuffer = gl.createBuffer(); gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, indicesBuffer ); gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW ); // 顶点位置、颜色 var verticesColors = new Float32Array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 0.0, 1.0, -1.0, -1.0, 1.0, 1.0, 0.0, 0.0, 1.0, -1.0, 1.0, 1.0, 1.0, 0.0, 1.0, -1.0, -1.0, 0.0, 1.0, 0.0, 1.0, 1.0, -1.0, 0.0, 1.0, 1.0, -1.0, 1.0, -1.0, 0.0, 0.0, 1.0, -1.0, -1.0, -1.0, 0.0, 0.0, 0.0 ]), element_size = verticesColors.BYTES_PER_ELEMENT; var verticesColorBuffer = gl.createBuffer(); gl.bindBuffer( gl.ARRAY_BUFFER, verticesColorBuffer ); gl.bufferData( gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW ); // 位置 var a_Position = gl.getAttribLocation(gl.program, 'a_Position'); gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, element_size * 6, 0); gl.enableVertexAttribArray(a_Position); // 颜色 var a_Color = gl.getAttribLocation(gl.program, 'a_Color'); gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, element_size * 6, element_size * 3); gl.enableVertexAttribArray(a_Color); return indices.length; }

每个面同一种颜色

上面的代码,每个顶点都背多个面共用,但它只对应一种颜色,导致每个面的颜色都呈过渡状态。怎样才能使得个面都只有一种颜色呢? —— 顶点不共用,每一个面都重新单独设置4个顶点,虽然有顶点的位置是一样的,但颜色不一样。

var vertices = new Float32Array([ 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0 ]), colors = new Float32Array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.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, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0 ]), indices = new Uint8Array([ 0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 8, 9, 10, 8, 10, 11, 12, 13, 14, 12, 14, 15, 16, 17, 18, 16, 18, 19, 20, 21, 22, 20, 22, 23 ]);

画出的正方形每面都一种颜色,但这时也定义了24个的点数据,跟 gl.drawArrays() 比起来数据并没有少, 所以要结合具体情况来作选择。

旋转动画

逻辑和原理跟 rotation 一致。

function init(){ gl.clearColor( 0.0, 0.0, 0.0, 1.0 ); gl.enable( gl.DEPTH_TEST ); var mvpMatrix = new Matrix4(); mvpMatrix.setPerspective(45, canvas.width/canvas.height, 1, 100); mvpMatrix.lookAt(2, 2, 7, 0, 0, 0, 0, 1, 0); var u_mvpMatrix = gl.getUniformLocation(gl.program, 'u_mvpMatrix'); var n = initVertexBuffers(gl); // 旋转动画 var angle_step = 1/2; animate(); function animate(){ changeAngle( gl, u_mvpMatrix ); gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT ); gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0); requestAnimationFrame( animate ); } function changeAngle( gl, location ){ mvpMatrix.rotate( angle_step, 0, 1, 0); gl.uniformMatrix4fv(location, false, mvpMatrix.elements); } }