WebGL之旅(五)复合变换
2017-07-20 22:55
120 查看
复合变换,即对一个图形进行多次变换。
那么:
平移后的坐标t = 平移矩阵 x 原始坐标g
缩放后的G = 缩放矩阵 x 平移后的坐标t
所以:
缩放后的G = 缩放矩阵 x (平移矩阵 x 原始坐标g) = (缩放矩阵 x 平移矩阵) x 原始坐标g
可见,对于多次变换,可以将变换矩阵相乘得到一个最终的矩阵(叫模型矩阵),然后传给顶点着色器做最终的运算。
如图:
这里主要涉及到一个向量乘法的运算transposeMatrix。
因为是用列主序表示的,所以想把列主序转转置成行主序,然后在计算,计算完之后再转成列主序返回。
一 复合变换原理
比如,对图像g进行一次平移,然后进行一次缩放,求最终的得到图像g。那么:
平移后的坐标t = 平移矩阵 x 原始坐标g
缩放后的G = 缩放矩阵 x 平移后的坐标t
所以:
缩放后的G = 缩放矩阵 x (平移矩阵 x 原始坐标g) = (缩放矩阵 x 平移矩阵) x 原始坐标g
可见,对于多次变换,可以将变换矩阵相乘得到一个最终的矩阵(叫模型矩阵),然后传给顶点着色器做最终的运算。
示例
/** * 复合变换 * xu.lidong@qq.com * */ // 顶点着色器源码 var vertexShaderSrc = ` attribute vec4 a_Position;// 接收传入位置坐标,必须声明为全局 uniform mat4 u_Mat;// 旋转矩阵 void main(){ gl_Position = u_Mat * a_Position; }`; // 片段着色器源码 var fragmentShaderSrc = ` void main(){ gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);// gl_FragColor 内置变量,表示片元颜色,必须赋值 }`; // 初始化使用的shader function initShader(gl) { var vertexShader = gl.createShader(gl.VERTEX_SHADER);// 创建顶点着色器 gl.shaderSource(vertexShader, vertexShaderSrc);// 绑定顶点着色器源码 gl.compileShader(vertexShader);// 编译定点着色器 var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);// 创建片段着色器 gl.shaderSource(fragmentShader, fragmentShaderSrc);// 绑定片段着色器源码 gl.compileShader(fragmentShader);// 编译片段着色器 var shaderProgram = gl.createProgram();// 创建着色器程序 gl.attachShader(shaderProgram, vertexShader);// 指定顶点着色器 gl.attachShader(shaderProgram, fragmentShader);// 指定片段着色色器 gl.linkProgram(shaderProgram);// 链接程序 gl.useProgram(shaderProgram);//使用着色器 gl.program = shaderProgram; } function main() { var canvas = document.getElementById("container"); var gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl"); initShader(gl);// 初始化着色器 var n = initVertexBuffers(gl);// 初始化顶点 // 平移矩阵,列主序 var tran = getTranslationMatrix(0.5, 0.5, 0.0); // 旋转矩阵,列主序 var rot = getRotationMatrix(Math.PI * 0.5, 0.0, 0.0, 1.0); // 缩放矩阵,列主序 var scale = getScaleMatrix(0.5, 0.5, 1.0); var mat = multiMatrix44(scale, tran); mat = multiMatrix44(mat, rot); var u_Mat = gl.getUniformLocation(gl.program, 'u_Mat'); gl.uniformMatrix4fv(u_Mat, false, mat); gl.clearColor(0.0, 0.0, 0.0, 1.0);// 指定清空canvas的颜色 gl.clear(gl.COLOR_BUFFER_BIT);// 清空canvas gl.drawArrays(gl.TRIANGLES, 0, n); } function initVertexBuffers(gl) { var vertices = new Float32Array([ 0, 0.5, -0.5, -0.5, 0.5, -0.5, ]); var vertexBuffer = gl.createBuffer();// 创建缓冲区对象 gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);// 将缓冲区对象绑定到目标 gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);// 向缓冲区中写入数据 var a_Position = gl.getAttribLocation(gl.program, "a_Position");// 获取a_Position变量 gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);// 将缓冲区对象分配给a_Position gl.enableVertexAttribArray(a_Position);// 链接a_Position与分配给他的缓冲区对象 return vertices.length / 2; } /** * 由平移向量获取平移矩阵 * */ function getTranslationMatrix(x, y, z) { return 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, x, y, z, 1.0, ]); } /** * 由旋转弧度和旋转轴获取旋转矩阵 * */ function getRotationMatrix(rad, x, y, z) { if (x > 0) { // 绕x轴的旋转矩阵 return new Float32Array([ 1.0, 0.0, 0.0, 0.0, 0.0, Math.cos(rad), -Math.sin(rad), 0.0, 0.0, Math.sin(rad), Math.cos(rad), 0.0, 0.0, 0.0, 0.0, 1.0, ]); } else if (y > 0) { // 绕y轴的旋转矩阵 return new Float32Array([ Math.cos(rad), 0.0, -Math.sin(rad), 0.0, 0.0, 1.0, 0.0, 0.0, Math.sin(rad), 0.0, Math.cos(rad), 0.0, 0.0, 0.0, 0.0, 1.0, ]); } else if(z > 0) { // 绕z轴的旋转矩阵 return new Float32Array([ Math.cos(rad), Math.sin(rad), 0.0, 0.0, -Math.sin(rad), Math.cos(rad), 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, ]); } else { // 没有指定旋转轴,报个错,返回一个单位矩阵 console.error("error: no axis"); return 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, ]); } } /** * 由缩放因子获取缩放矩阵 * */ function getScaleMatrix(xScale, yScale, zScale) { return new Float32Array([ xScale, 0.0, 0.0, 0.0, 0.0, yScale, 0.0, 0.0, 0.0, 0.0, zScale, 0.0, 0.0, 0.0, 0.0, 1.0, ]); } /** * 1 x 1 向量点乘 * */ function dotMultiVector(v1, v2) { var res = 0; for (var i = 0; i < v1.length; i++) { res += v1[i] * v2[i]; } return res; } /** * 4 x 4 矩阵的转置 * */ function transposeMatrix(mat) { var res = new Float32Array(16); for (var i = 0; i < 4; i++) { for (var j = 0; j < 4; j++) { res[i * 4 + j] = mat[j * 4 + i]; } } return res; } /** * 4 x 4 矩阵乘法 * */ function multiMatrix44(m1, m2) { var mat1 = transposeMatrix(m1); var mat2 = transposeMatrix(m2); var res = new Float32Array(16); for (var i = 0; i < 4; i++) { var row = [mat1[i * 4], mat1[i * 4 + 1], mat1[i * 4 + 2], mat1[i * 4 + 3]]; for (var j = 0; j < 4; j++) { var col = [mat2[j], mat2[j + 4], mat2[j + 8], mat2[j + 12]]; res[i * 4 + j] = dotMultiVector(row, col); } } return transposeMatrix(res); }
如图:
这里主要涉及到一个向量乘法的运算transposeMatrix。
因为是用列主序表示的,所以想把列主序转转置成行主序,然后在计算,计算完之后再转成列主序返回。
相关文章推荐
- WebGL学习系列-基本图形变换
- 计算机图形学(四)几何变换_4_二维复合变换_2_矩阵合并特性
- WebGL笔记_三维坐标变换(一)
- 计算机图形学(四)几何变换_4_二维复合变换(上)
- WebGL 绘制和变换
- [WebGL入门]十三,minMatrix.js和坐标变换矩阵
- 计算机图形学(四)几何变换_4_二维复合变换_5_其他二维变换_1_反射
- [置顶] 第四节 WebGL中的图形变换:旋转、平移和缩放
- Android 高级Drawable资源---复合Drawable----变换Drawable
- 计算机图形学(四)几何变换_4_二维复合变换_3_通用二维复合变换和计算效率
- (16)二维图形复合变换
- Android 高级Drawable资源---复合Drawable----变换Drawable---RotateDrawable用法实例
- 计算机图形学(四)几何变换_4_二维复合变换_5_其他二维变换_2_错切
- WebGL之旅(四)简单变换
- WebGL学习系列-基础矩阵变换
- 图像的空间复合变换
- 计算机图形学(四)几何变换_4_二维复合变换_4_二维刚体变换
- 变换不变性 转自林达华
- 复合索引 优化和适用范围
- Android RxJava线程变换初探