1、Matrix4函数库
此处需要了解一个矩阵函数库cuon-matrix.js
这是 《webgl编程指南》
作者写的一个库,它封装了一些简单易用的方法,来实现一些复杂繁琐的矩阵计算操作;
代码地址:
https://gitee.com/ithanmang/webgl-notes
具体方法如下表:
Matrix4
对象有两种方法,一种是名称中带有set
和一种不带set
的;
- 包含
set
的 – 会根据参数计算出 变换矩阵,然后将变换矩阵写入到 自身中; - 不含
set
的 – 会现根据参数计算出变换矩阵,然后将自身与刚刚计算得到的变换矩阵相乘;
2、基本变换
使用上面的方法可以方便进行矩阵变换:
如平移操作
var xformMatrix = new Matrix4();
// 平移
xformMatrix.setTranslate(0.5, 0.5, 0.5);
// 将变换后的矩阵传递 其中的 elements 是列主序的类型化数组
gl.uniformMatrix4fv(u_xformMatrix, false, xformMatrix.elements);
顶点着色器接收就可以了
attribute vec4 a_Position;
uniform mat4 u_xformMatrix;
void main(){
// 变换后的坐标 = 变换矩阵 * 原始坐标
gl_Position = u_xformMatrix * a_Position;
}
3、复合变换
例如我们想要先平移然后再旋转:
显然,这包括了两种变换,先进行平移变换然后再进行旋转变换;
首先,先进行平移变换
- <平移后的坐标> = <平移矩阵> * <原始坐标>
然后,平移后的坐标再进行旋转变换
- <平移旋转后的坐标> = <旋转矩阵> * <平移后的坐标>
组合起来就是
- <平移旋转后的坐标> = <旋转矩阵> * ( <平移矩阵> * <原始坐标>)
所以可以先计算<旋转矩阵> * <平移矩阵>
,然后将多次变换后的矩阵乘以 原始坐标就可以实现复合变换;
一个模型可能经过了多次变换,将这些变换全部复合成一个等效的变换,也就得到了 模型变换,或称 建模变换,所以,模型变换的矩阵称为 模型矩阵
所以此时着色器可以这样写
attribute vec4 a_Position;
// 声明一个模型矩阵
uniform mat4 u_ModelMatrix;
void main(){
// 复合变换后的坐标 = 模型矩阵 * 原始坐标
gl_Position = u_ModelMatrix * a_Position;
}
使用Matrix4.js
中的方法可以这样写,先平移然后再旋转
- 先平移:
xformMatrix.setTranslate(0.5, 0.5, 0)
- 再旋转:
xformMatrix.rotate(45, 0, 0, 1)
先旋转再平移,和先平移再旋转计算的模型矩阵不一定相同
实例代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>先平移后旋转</title>
<link rel="stylesheet" href="../css/common.css">
</head>
<body>
<canvas id="webgl" width="512" height="512"></canvas>
</body>
<script src="../lib/webgl-utils.js"></script>
<script src="../lib/webgl-debug.js"></script>
<script src="../lib/cuon-utils.js"></script>
<script src="../lib/cuon-matrix.js"></script>
<script>
// 顶点着色器
var vertex_shader_source = '' +
'attribute vec4 a_Position;' +
'uniform mat4 u_ModelMatrix;' +
'void main() {' +
' gl_Position = u_ModelMatrix * a_Position;' +
'}';
// 片元着色器
var fragment_shader_source = '' +
'void main(){' +
' gl_FragColor = vec4(0.5, 0.0, 0.5, 1.0);' +
'}';
(function () {
// 获取canvas对象
var canvas = document.getElementById('webgl');
// 获取webgl 上下文对象
var gl = getWebGLContext(canvas);
// 初始化着色器
if (!initShaders(gl, vertex_shader_source, fragment_shader_source)) {
console.log('初始化着色器失败!');
return false;
}
// 设置顶点位置
var n = initVertexBuffer(gl);
if (n < 0) {
console.log('顶点写入缓存失败!');
return false;
}
// 旋转角度
var ANGLE = 90.0;
// 创建旋转矩阵对象
var modelMatrix = new Matrix4();
modelMatrix.setRotate(ANGLE, 0, 0, 1);
modelMatrix.translate(0.5, 0.5, 0);
// 通过旋转矩阵传递给顶点着色器
var u_ModelMatrix = gl.getUniformLocation(gl.program, 'u_ModelMatrix');
if (!u_ModelMatrix) {
console.log('获取uniform变量失败!');
return false;
}
gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements);
// 设置清空颜色
gl.clearColor(0.0, 0.5, 0.5, 1.0);
// 清空canvas
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, n);
// 将顶点信息写入缓存区
function initVertexBuffer(gl) {
var vertices = new Float32Array([
0.0, 0.25, 0.25, -0.25, -0.25, -0.25
]);
var n = vertices.length / 2;
// 创建缓冲区对象
var vertexBuffer = gl.createBuffer();
if (!vertexBuffer) {
console.log('创建缓冲区对象失败!');
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');
if (a_Position < 0) {
console.log('获取attribute变量失败!');
return -1;
}
// 将缓冲区对象分配给attribute变量
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
// 开启attribute变量
gl.enableVertexAttribArray(a_Position);
return n;
}
}());
</script>
</html>