手写各种矩阵:
//矩阵 var vShader = ` attribute vec4 a_Position; uniform mat4 u_xformMatrix; void main(){ gl_Position = u_xformMatrix * a_Position; } `; var fShader = ` void main(){ gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); } `; function main(){ //获取canvas元素 var canvas = document.getElementById('webgl'); //获取webgl上下文 var gl = getWebGLContext(canvas); if(!gl){ console.log('Failed to get the rendering context for WebGL!'); return; } //初始化着色器 if(!initShaders(gl,vShader,fShader)){ console.log('Failed to initialize shaders.'); return; } var n = initVertexBuffers(gl); if(n < 0){ console.log('Failed to set the positions of the vertices!'); return; } //用指定颜色填充webgl容器,就是设置背景 gl.clearColor(0.4, 0.5, 0.0, 1.0); gl.clear(gl.COLOR_BUFFER_BIT); /*手动写各种矩阵*/ var ANGLE = 180.0; var radian = Math.PI * ANGLE / 180.0; var cosB = Math.cos(radian),sinB = Math.sin(radian); //旋转矩阵 var transform = [ cosB,-sinB,0.0,0.0, sinB,cosB,0.0,0.0, 0.0,0.0,1.0,0.0, 0.0,0.0,0.0,1.0 ]; //平移矩阵 var transform = [ 1.0,0.0,0.0,0.5, 0.0,1.0,0.0,0.5, 0.0,0.0,1.0,0.0, 0.0,0.0,0.0,1.0 ]; //旋转+平移矩阵 var transform = [ cosB,-sinB,0.0,0.5, sinB,cosB,0.0,0.5, 0.0,0.0,1.0,0.0, 0.0,0.0,0.0,1.0 ]; //缩放矩阵 var Sx = 0.5,Sy = 0.5,Sz = 0.5; var transform = [ Sx,0.0,0.0,0.0, 0.0,Sy,0.0,0.0, 0.0,0.0,Sz,0.0, 0.0,0.0,0.0,1.0 ]; //将行主序矩阵转化为列主序矩阵(webgl只支持列主序矩阵) var xformMatrix = new Float32Array(rTc(transform)); //至此,适用于顶点着色器的变化矩阵已经写好 /*行主序转为列主序(webgl是列主序的)*/ function rTc(arr){ var size = arr.length/4; var newarr = []; for (var i = 0; i < size; i++) { newarr.push(arr[i]); newarr.push(arr[4+i]); newarr.push(arr[8+i]); newarr.push(arr[12+i]); } return newarr; } var u_xformMatrix = gl.getUniformLocation(gl.program,'u_xformMatrix'); gl.uniformMatrix4fv(u_xformMatrix,false,xformMatrix); gl.drawArrays(gl.TRIANGLES,0,n); function initVertexBuffers(gl){ var vertices = new Float32Array([ 0.0,0.5,-0.5,-0.5,0.5,-0.5 ]); var n = 3;//点的个数 //创建缓冲区对象 var vertexBuffer = gl.createBuffer(); if(!vertexBuffer){ console.log('Failed to create the buffer object!'); return -1; } //将缓冲区对象绑定到目标ARRAY_BUFFER gl.bindBuffer(gl.ARRAY_BUFFER,vertexBuffer); //往ARRAY_BUFFER gl.bufferData(gl.ARRAY_BUFFER,vertices,gl.STATIC_DRAW); //获取shaderProgram中attribute变量‘a_Position’的地址 var a_Position = gl.getAttribLocation(gl.program,'a_Position'); if (a_Position < 0) { console.log('Failed to get the storage location of a_Position'); return -1; } //将缓冲区对象分配给a_Position变量 gl.vertexAttribPointer(a_Position,2,gl.FLOAT,false,0,0); //开启着色器对缓冲区数据的访问 gl.enableVertexAttribArray(a_Position); return n; } } main();
利用webgl编程指南作者提供的矩阵库:
//矩阵 var vShader = ` attribute vec4 a_Position; uniform mat4 u_xformMatrix; void main(){ gl_Position = u_xformMatrix * a_Position; } `; var fShader = ` void main(){ gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); } `; function main(){ //获取canvas元素 var canvas = document.getElementById('webgl'); //获取webgl上下文 var gl = getWebGLContext(canvas); if(!gl){ console.log('Failed to get the rendering context for WebGL!'); return; } //初始化着色器 if(!initShaders(gl,vShader,fShader)){ console.log('Failed to initialize shaders.'); return; } var n = initVertexBuffers(gl); if(n < 0){ console.log('Failed to set the positions of the vertices!'); return; } //用指定颜色填充webgl容器,就是设置背景 gl.clearColor(0.4, 0.5, 0.0, 1.0); gl.clear(gl.COLOR_BUFFER_BIT); var ANGLE = 180.0; var xformMatrix = new Matrix4(); //设置旋转矩阵,旋转轴为(0,0,1),即Z轴 xformMatrix.setRotate(ANGLE,0,0,1); xformMatrix.translate(0.5,0,0); //先设置旋转矩阵,在此基础上乘以平移矩阵 //最后结果是 旋转矩阵x平移矩阵,就是先平移再旋转 // xformMatrix.setTranslate(0.5,0,0); // xformMatrix.rotate(ANGLE,0,0,1); //先设置平移矩阵,在此基础上乘以旋转矩阵 //最后结果是 平移矩阵x旋转矩阵,就是先旋转再平移 gl.uniformMatrix4fv(u_xformMatrix,false,xformMatrix.elements); gl.drawArrays(gl.TRIANGLES,0,n); function initVertexBuffers(gl){ var vertices = new Float32Array([ 0.0,0.5,-0.5,-0.5,0.5,-0.5 ]); var n = 3;//点的个数 //创建缓冲区对象 var vertexBuffer = gl.createBuffer(); if(!vertexBuffer){ console.log('Failed to create the buffer object!'); return -1; } //将缓冲区对象绑定到目标ARRAY_BUFFER gl.bindBuffer(gl.ARRAY_BUFFER,vertexBuffer); //往ARRAY_BUFFER gl.bufferData(gl.ARRAY_BUFFER,vertices,gl.STATIC_DRAW); //获取shaderProgram中attribute变量‘a_Position’的地址 var a_Position = gl.getAttribLocation(gl.program,'a_Position'); if (a_Position < 0) { console.log('Failed to get the storage location of a_Position'); return -1; } //将缓冲区对象分配给a_Position变量 gl.vertexAttribPointer(a_Position,2,gl.FLOAT,false,0,0); //开启着色器对缓冲区数据的访问 gl.enableVertexAttribArray(a_Position); return n; } } main();
在顶点着色器中,我们添加了一个矩阵参数,通过变化矩阵来改变顶点的坐标,替代了之前的复杂的运算,关于矩阵:
矩阵运算:
平移矩阵:
旋转矩阵:
注意:
①webGL中矩阵是列主序的,但是我们通常接触的高数中的矩阵都是行主序的,行主序的矩阵看上去也比较符合人的习惯,所以我们在这里,手写矩阵的时候,写成行主序,但是传给webGL的时候,要将其转化为列主序的,文中已经列出了我自己编写的转化函数:
/*行主序转为列主序(webgl是列主序的)*/ function rTc(arr){ var size = arr.length/4; var newarr = []; for (var i = 0; i < size; i++) { newarr.push(arr[i]); newarr.push(arr[4+i]); newarr.push(arr[8+i]); newarr.push(arr[12+i]); } return newarr; }
②在矩阵库的帮助下,我们写起矩阵尤为方便,但要注意,关于set***和不加set前缀的方法的区别
set***为一个矩阵对象初始化一个矩阵M1,多次set***的话,结果只包含最后一次set的矩阵;
不加set的方法,在原先初始化了矩阵的矩阵基础上M1 x M2,多个的结果是M1 x M2 x M3 x......注意顺序;
矩阵乘法不适用交换律,所以先后顺序不同的话,最终效果也会不一样,文中注释已说明。