WebGL编程指南四:利用矩阵进行平移旋转缩放变换

目标:掌握利用矩阵位移,旋转,缩放基本图形

准备

引入cuon-matrix.js矩阵库

基本概念

  • Matrix4类 cuon-matrix.js中为我们提供了Matrix4类,Matrix4对象(实例)表示一个4*4的矩阵,该对象内部使用类型化数组Floated32Array来存储矩阵的元素
  • 旋转
    为描述一个旋转,你必须指明1.旋转轴(图形将围绕旋转轴旋转); 2.旋转方向(方向:顺时针或逆时针); 3.旋转角度(图形旋转经过的角度)。
    在旋转中,关于“逆时针”的约定是:如果β是正值,观察者在Z轴正半轴某处,视线沿着Z轴负方向进行观察,那个看到的物体就是逆时针的,这种情况又可称为正旋转。我们也可以使用右手来确定旋转方向:右手握拳,大拇指伸直并指向旋转轴正方向,那么右手其余几个手指就指明了旋转的方向,因此正方向又可以称为右手法则旋转。本文默认使用右手法则旋转
    本文旋转中这样描述旋转:绕Z轴,逆时针旋转β角度;
  • 必须掌握的方法
    Matrix4对象所支持的方法和属性
方法和属性名称描述
Matrix4.setIdentity()将Matrix4实例初始化为单位阵
Matrix4.setTranslate(x,y,z)将Matrix4实例设置为平移变换矩阵,在x轴上平移的距离为x,在y轴上平移的距离为y,在z轴上平移的距离为z
Matrix4.setRotate(angle,x,y,z)将Matrix4实例设置为旋转变换矩阵,旋转的角度为angle,旋转轴为(x,y,z),旋转轴(x,y,z)无需归一化
Matrux4,setScale(x,y,z)将Matrix4实例设置为缩放变换矩阵,在三个轴上的缩放因子分别为x,y,z
Matrix4.translate(x,y,z)将Matrix4实例乘以一个平移变换矩阵(该平移矩阵在x轴上平移的距离为x,在y轴上平移的距离为y,在z轴上平移的距离为z),所得的结果还存储在Matrix4中
Matrix4.rotate(angle,x,y,z)将Matrix4实例乘以一个旋转变换矩阵(该旋转矩阵旋转角度为angle,旋转轴为(x,y,z),旋转轴(x,y,z)无需归一化),所得的结果还是存储在Matrix4中
Matrix4.scale(x,y,z)将Matrix4实例乘以一个缩放变换矩阵(该缩放矩阵在三个轴上的缩放因子分别为x,y,z),所得的结果还存储在Matrix4中。
Matrix4.set(m)将Matrix4实例设置为m,m必须也是一个Matrix4实例
Matrix4.elements类型化数组(Float32Array)包含了Matrix4实例的矩阵元素

上边的表格中,Matrix4对象有两种方法,一种方法名称中含有前缀set,这一种会根据参数计算出变换矩阵,然后将矩阵写入自身中,另一种不含set,这种会先根据参数计算出变换矩阵,然后将自身与刚刚计算得到的变换矩阵相乘,然后把最终得到的结果写入Matrix4对象中
单位阵在矩阵乘法中的行为,就像数字1在乘法中的行为一样,将一个矩阵乘以单位阵,得到的结果和原矩阵完全相同。在单位阵中,对角线上的元素为1.0,其余元素为0.0

  • 变换等式
    旋转后 的坐标 = 旋转矩阵 * 原始坐标(矢量)
    缩放后 的坐标 = 缩放矩阵 * 原始坐标(矢量)
    平移后 的坐标 = 平移矩阵 * 原始坐标(矢量)
    平移后旋转 的坐标 = 旋转矩阵 * 平移后的坐标(矢量)
    = 旋转矩阵 * (平移矩阵 * 原始坐标)
    = (旋转矩阵 * 平移矩阵)* 原始坐标
  • 新坐标 = 变换矩阵 * 原始坐标
    gl_Position = u_xformMatrix * a_Position;

一个模型可能经过了多次变换,将这些变换全部复合成一个等效的变换,就得到了模型变换,模型变换的矩阵称为模型矩阵。上式中(旋转矩阵*平移矩阵)就称为模型矩阵

方法

gl.uniformMatrix4fv(location, transpose, array)将array表示的4*4矩阵分配给location指定的uniform变量。transpose在WebGL中必须指定为false

示例程序

// 示例一(利用矩阵方法平移旋转三角形)
var VSHADER_SOURCE = `
	attribute  vec4 a_Position;\n
	uniform mat4 u_ModelMatrix;\n
	void main(){\n
		gl_Position = u_ModelMatrix * a_Position;\n
	}\n
`
var FSHADER_SOURCE = `
	void main(){\n
		gl_FragColor = vec4(1.0,0.0,0.0,1.0);\n
	}
`
var ANGLE = 60.0;// 旋转角度
var Tx = 0.5;//平移

function main () {
	var canvas = document.getElementById('webgl');
	var gl = getWebGLContext(canvas);
	gl.clearColor(0.0,0.0,0.0,1.0);
	if(!initShaders(gl,VSHADER_SOURCE,FSHADER_SOURCE)) {
		return;
	}
	
	var n = initVertexBuffers(gl);
	gl.clear(gl.COLOR_BUFFER_BIT);// 清空缓存区
	gl.drawArrays(gl.LINE_LOOP,0,n);// 绘制三角形
}
main();
function initVertexBuffers(gl) {
	var vertices = new Float32Array([0.0,0.5,-0.5,-0.5,0.5,-0.5]); // 三个顶点坐标数据
	var n = 3; //顶点个数为3
	var vertexBuffer = gl.createBuffer();
	if (!vertexBuffer) {
		console.log('Failed to create the buffer object');
		return -1;
	}
	
	// 将顶点数据写入缓存区
	gl.bindBuffer(gl.ARRAY_BUFFER,vertexBuffer);
	gl.bufferData(gl.ARRAY_BUFFER,vertices,gl.STATIC_DRAW);
	var a_Position = gl.getAttribLocation(gl.program,'a_Position');
	gl.vertexAttribPointer(a_Position,2,gl.FLOAT,false,0,0);
	gl.enableVertexAttribArray(a_Position);

	//模型矩阵
	var modelMatrix = new Matrix4();
	/* 先平移后旋转
	modelMatrix.setRotate(ANGLE,0,0,1);
	modelMatrix.translate(Tx,0,0);
	*/
	/* 先旋转后平移 */
	modelMatrix.setTranslate(Tx,0,0);
	modelMatrix.rotate(ANGLE,0,0,1);
	
	// 将模型矩阵传给attribute变量
	var u_ModelMatrix = gl.getUniformLocation(gl.program,'u_ModelMatrix');
	gl.uniformMatrix4fv(u_ModelMatrix,false,modelMatrix.elements);
	return n;
}
//示例二(分别平移、旋转、缩放三角形,自己手写矩阵,仅供参考)
var VSHADER_SOURCE = `
   attribute  vec4 a_Position;\n
   uniform mat4 u_xformMatrix;\n
   void main(){\n
   	gl_Position = u_xformMatrix * a_Position;\n
   }\n
`
var FSHADER_SOURCE = `
   void main(){\n
   	gl_FragColor = vec4(1.0,0.0,0.0,1.0);\n
   }
`
var ANGLE = 90.0;
function main () {
   var canvas = document.getElementById('webgl');
   var gl = getWebGLContext(canvas);
   gl.clearColor(0.0,0.0,0.0,1.0);
   if(!initShaders(gl,VSHADER_SOURCE,FSHADER_SOURCE)) {
   	return;
   }
   var n = initVertexBuffers(gl);
   gl.clear(gl.COLOR_BUFFER_BIT);// 清空缓存区
   gl.drawArrays(gl.LINE_LOOP,0,n); // 绘制三角形
}
main();
function initVertexBuffers(gl) {
   var vertices = new Float32Array([0.0,0.5,-0.5,-0.5,0.5,-0.5]); // 三个顶点的坐标数据
   var n = 3;// 顶点个数为3
   var vertexBuffer = gl.createBuffer();
   if (!vertexBuffer) {
   	console.log('Failed to create the buffer object');
   	return -1;
   }
   
   // 将顶点数据写入缓存区
   gl.bindBuffer(gl.ARRAY_BUFFER,vertexBuffer);
   gl.bufferData(gl.ARRAY_BUFFER,vertices,gl.STATIC_DRAW);
   var a_Position = gl.getAttribLocation(gl.program,'a_Position');
   gl.vertexAttribPointer(a_Position,2,gl.FLOAT,false,0,0);
   gl.enableVertexAttribArray(a_Position);

   /* 旋转矩阵
   var radian = Math.PI * ANGLE / 180.0;
   var cosB = Math.cos(radian);
   var sinB = Math.sin(radian);

   var xformMatrix = new Float32Array([
   		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 Tx = 0.5,Ty = 0.5,Tz = 0.5;
   var xformMatrix = new Float32Array([
   		1.0, 0.0, 0.0, 0.0,
   		0.0, 1.0, 0.0, 0.0,
   		0.0, 0.0, 1.0, 0.0,
   		Tx,  Ty,  Tz,  1.0
   	])
   */

   // 缩放矩阵
   var Sx = 1.0, Sy = 1.5, Sz = 1.0;
   var xformMatrix = new Float32Array([
   		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
   	])

	/* 无任何变换的矩阵
       var xformMatrix = new Float32Array([
           1.0,  0.0,  0.0, 0.0,
           0.0,  1.0,  0.0, 0.0,
           0.0,  0.0,  1.0, 0.0,
           0.0,  0.0,  0.0, 1.0
       ]);
    */
   // 将变换矩阵写入传给attribute变量
   var u_xformMatrix = gl.getUniformLocation(gl.program,'u_xformMatrix');
   gl.uniformMatrix4fv(u_xformMatrix,false,xformMatrix);
   return n;
}

详解矩阵【矩阵的算法是表达式算法的另一种表达方式】

矩阵和矢量的乘法

矢量就是由多个分量组成的对象,如顶点的坐标(0, 0.5, 1);
矩阵和矢量的乘法可以写成如下形式
在这里插入图片描述
可见将矩阵和矢量相乘,就可以获得一个新的矢量。注意矩阵的乘法不符合交换律,A*B不等于B*A
上式中的矩阵为3*3矩阵,矩阵右侧是一个由x,y,z组成的矢量(表示点的坐标)。注意:只有在矩阵的列数和矢量的行数相等时,才可以将两者相乘

矩阵和等式的转换

假设点p的坐标为(x, y, z, 1),平移之后的点p’ 的坐标为(x’, y’, z’, 1);
如下表示:
矩阵的乘法

在这里插入图片描述

矩阵的元素都是等式中的系数。平移矩阵必须为4*4矩阵,因为平移是加上一个常量,第四列均为常量。
一旦你熟悉这种矩阵表示法,进行变换就变得非常简单了。如果你不熟悉,你应当花点时间好好的理解它,变换矩阵的概念在三维图形学中非常重要

平移矩阵

在这里插入图片描述

缩放矩阵

在这里插入图片描述

旋转矩阵

旋转的数学表达式推导:
在这里插入图片描述
在这里插入图片描述
利用三角函数两角和公式,可得:
在这里插入图片描述

在这里插入图片描述
旋转矩阵为3*3矩阵,平移矩阵是4*4矩阵,为使他们阶数相同,比较如下等式:
在这里插入图片描述
在这里插入图片描述

JavaScript表示矩阵

JavaScript并没有专门表示矩阵的类型,你需要使用类型化数组Float32Array 存储矩阵的每个元素,但矩阵是二维的,其元素按照行和列进行排列,而数组是一维的,其元素只能排成一行。我们可以按照两种方式在数组中存储矩阵元素:按行主序(row major order)和按列主序(column major order)
在这里插入图片描述
WebGL和OpenGL一样,矩阵元素是按列主序存储在数组中的[a, e, i, m, b, f, j, n, c, …].

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值