本文纲要
- 矩阵和线性变换是什么?
- webgl 如何实现缩放和旋转?
- 平移不是线性变换,那该怎么办?
- webgl 如何实现平移?
明天的主菜是“矩阵”
在上一篇中咱们曾经实现了应用 webgl 绘制图形这个小指标《前端图形学从入门到放弃》001 画一个三角形
明天咱们来探讨一个新的话题矩阵
咱们都晓得空间中的点咱们能够用向量示意,例如二维立体中的点(1,1)就示意第一象限的点:
而多个点就能组成图形,这也是上一篇文章中咱们说过的。
理论生产中这些图形往往并不会固定在画面中不懂,例如咱们能够对图形进行旋转,缩放,挪动。
实际上这个过程就是将图形的顶点组进行了旋转,缩放,挪动,成为了新的顶点组,再由新的顶点组绘制成新的图形。
例如咱们要将由点 A(0,0),B(1,0),C(0,1)组成的三角形放大一倍,那么咱们很容易晓得放大后的点ÂḂĆ的坐标
Âx = Ax2 = 02 = 0
Ây = Ay2 = 02 = 0
Ḃx = Bx2 = 12 = 2
Ḃy = By2 = 02 = 0
Ćx = Cx2 = 02 = 0
Ćy = Cy2 = 12 = 2
数学家嫌这一番操作太过麻烦,而点又是能够写成向量模式的,要是能把操作简化成Â = M* A 的模式就再好不过了,于是
⎡ 2 0 ⎤
 = ⎪ ⎪ * A
⎣ 0 2 ⎦
真是一顿操作猛如虎,一句不懂二百五
解剖矩阵
举证代表了一种计算,如上咱们应用了一个二维矩阵
⎡ A B ⎤
⎣ C D ⎦
与一个二维向量相乘,会失去一个新的二维向量,计算公式如下
⎡ A B ⎤ ⎡x⎤ = ⎡ A*x + B*y ⎤
⎣ C D ⎦ ⎣y⎦ ⎣ C*x + D*y ⎦
当然矩阵也不仅仅能够和向量相乘也能够和举证相乘,矩阵也不仅仅能够是 2 2,也能够是 3 3,更能够是 n *m(n 代表行数,m 代表列数)。
两个矩阵能够相乘只须要,前一个矩阵的列数和后一个矩阵的函数相等即可。
例如 n m 的举证能够和 m l 的矩阵相乘,失去 n * l 的矩阵。
至于计算方法不是本文探讨的内容,举荐观看 3blue1brown 的视频。
缩放矩阵 与 旋转矩阵
而上文咱们看到的矩阵
⎡ 2 0 ⎤
⎣ 0 2 ⎦
就是一个把任意点放大两倍的矩阵,更个别的,如果能够写出缩放矩阵(n≠0)
⎡ n 0 ⎤ ⎡x⎤ = ⎡ n*x ⎤
⎣ 0 n ⎦ ⎣y⎦ ⎣ n*y ⎦
相比于缩放还有一种操作也很高频,那就是旋转。后面没有提到,矩阵的变换是线性的。什么叫做线性?也是是说同样的操作(放大 2 倍)对 A 点产生的成果,和对 B 点产生的成果(放大 2 倍)是一样的。
所以对于旋转矩阵咱们也能够找到非凡的点进行求解,从而失去广泛实用的矩阵
对于 x 轴上的点 a,旋转ø角后,能够用下图形容
咱们就失去了二维立体上的旋转矩阵
⎡ cosø -sinø ⎤
⎣ sinø cosø ⎦
webgl 和矩阵更配哟~
上面咱们把矩阵和 webgl 联合起来,让《前端图形学从入门到放弃》001 画一个三角形中咱们实现的三角形能够旋转与缩放
首先咱们在页面上增加两个滑块分辨实现旋转与缩放
<canvas id="canvas" width="1000" height="1000"></canvas>
<div id="control">
<input type="range" value=".75" min=".5" max="1" step="0.01" id="scale" value="0">
<input type="range" value="0" min="0" max="360" step="0.01" id="rotate" value="0">
</div>
因为旋转和缩放操作仅仅影响顶点地位,上面咱们之须要批改顶点着色器即可:
<script id="vertex-shader-2d" type="notjs">
attribute vec2 vertPosition;
attribute vec3 vertColor;
varying vec3 fragColor;
// 额定申明两个矩阵用于旋转和缩放
uniform mat2 scaleMatrix;
uniform mat2 rotateMatrix;
void main() {
fragColor = vertColor;
// 把顶点坐标与矩阵相乘,失去旋转和缩放后的新顶点,传给 gl
gl_Position = vec4(scaleMatrix*rotateMatrix*vertPosition,0.0,1.0);
}
</script>
这两个申明的变量也要在 js 中取出
...
var positionAttribLocation = gl.getAttribLocation(program, 'vertPosition');
var colorAttribLocation = gl.getAttribLocation(program, 'vertColor');
var scaleMatrix = gl.getUniformLocation(program, 'scaleMatrix');
var rotateMatrix = gl.getUniformLocation(program, 'rotateMatrix');
...
因为咱们冀望在滑动滑块时,页面实时变动,因而须要一个 loop 函数来实现这所有:
....
gl.useProgram(program);
gl.drawArrays(gl.TRIANGLES, 0, 6);
loop(gl, rotateMatrix, scaleMatrix);
}
...
loop 函数:
var scaleNode = document.querySelector("#scale");
var rotateNode = document.querySelector("#rotate");
function loop(gl, rotateMatrix, scaleMatrix) {
var angle = rotateNode.value/180*Math.PI;
var scale = scaleNode.value;
var sin = Math.sin(angle);// 旋转角度正弦值
var cos = Math.cos(angle);// 旋转角度余弦值
var myArr = new Float32Array([cos, -sin, sin, cos,]);
var scaleArr = new Float32Array([scale, 0, 0, scale,]);
gl.uniformMatrix2fv(rotateMatrix, false, myArr);
gl.uniformMatrix2fv(scaleMatrix, false, scaleArr);
gl.clearColor(0.75, 0.85, 0.8, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
requestAnimationFrame(function () {loop(gl, rotateMatrix, scaleMatrix);
});
gl.drawArrays(gl.TRIANGLES, 0, 3);
}
功败垂成:
教练我动不了了
不晓得各位看官有没有发现,在矩阵这套线性变动下,咱们没方法做平移操作。因为作为原点的 o(0,0)不管乘以什么矩阵,后果都还是本人。然而平移操作是日常工作中极其常见的操作,不能平移甚至无奈实现拖拽!
难道图形学之路就此 gg?
但天无绝人之路,只有零点不是零点我就能够挪动它,对于二维立体,我能够把它看作三维世界中一个不过原点的立体,本来的 (x,y) 变为 (x,y,1)
此时就能够实现平移
依据上文,咱们曾经理解的矩阵常识,不难写出
而这种通过 n + 1 维实现了 n 维线性变换外加挪动操作的变换,就被称为 齐次变换
。
webgl 和齐次变换更配哟~
上面咱们持续革新原有的 webgl 代码!
首先咱们还须要退出两个滑块别离管制,图形高低和左右静止
<div id="control">
...
<input type="range" value="0" min="-0.5" max="0.5" step="0.01" id="tranX">
<input type="range" value="0" min="-.5" max=".5" step="0.01" id="tranY">
</div>
因为齐次变换将所有的矩阵都升维了,咱们须要革新定点着色器。
<script id="vertex-shader-2d" type="notjs">
...
// 将本来二维矩阵定义为三维
uniform mat3 scaleMatrix;
uniform mat3 rotateMatrix;
uniform mat3 transformMatrix;
void main() {
fragColor = vertColor;
vec3 v = rotateMatrix*scaleMatrix*transformMatrix*vec3(vertPosition,1.0);
// 因为咱们之须要 x,y 把他们取出即可
gl_Position = vec4(v.xy,0.0,1.0);
}
</script>
因为矩阵从二维变为三维,取出的变量也须要从新定义为三维:
...
var trMatrix = gl.getUniformLocation(program,'transformMatrix');
var scaleMatrix = gl.getUniformLocation(program, 'scaleMatrix');
var rotateMatrix = gl.getUniformLocation(program, 'rotateMatrix');
...
// 获取滑块
var tranXNode = document.querySelector("#tranX");
var tranYNode = document.querySelector("#tranY");
// 批改 loop 函数
...
loop(gl, rotateMatrix, scaleMatrix,trMatrix);
}
function loop(gl, rotateMatrix, scaleMatrix,trMatrix) {
...
var myArr = new Float32Array([cos, -sin, 0 , sin, cos, 0,0,0,1]);
var scaleArr = new Float32Array([scale, 0, 0,0, scale,0,0,0,1]);
var tranArr = new Float32Array([1,0,0,0,1,0,tranXNode.value,tranYNode.value,1]);
// console.log(tranXNode.value);
gl.uniformMatrix3fv(rotateMatrix, false, myArr);
gl.uniformMatrix3fv(scaleMatrix, false, scaleArr);
gl.uniformMatrix3fv(trMatrix, false, tranArr);
....
功败垂成:
下期预报
我想二维的世界,大家也腻了,下篇咱们将进入三维世界,并说说光线是如何影响物体的