WebGL 理论基础 - 二维矩阵


此文上接一系列文章,先从基础概念开始,上一篇是物体缩放。

之前的三篇文章讲了如何对二维物体进行平移,旋转,和 缩放。每种变换都改变了着色器并且这些变换还受先后顺序影响。在前例中我们先缩放,再旋转,最后平移,如果执行顺序不同结果也不同。

例如这是缩放 2, 1 ,旋转30度,然后平移 100, 0 的结果。

WebGL 理论基础 - 二维矩阵_第1张图片

这是平移 100, 0 ,旋转30度,然后缩放 2, 1 的结果。

WebGL 理论基础 - 二维矩阵_第2张图片

结果截然不同,更糟的是,针对第二种情况中的转换顺序,需要写一个新的着色器。

有些聪明的人可能已经想到了矩阵,对于二维我们使用 3x3 的矩阵,3x3 的矩阵就像是有 9 个格子的格网。

在计算的时候我们将位置坐标沿着矩阵列的方向依次相乘再将结果加起来。我们的位置信息只有两个值, x 和 y 。但是要进行运算需要三个值,所以我们将第三个值赋值为 1 。

在这个例子中结果将是

WebGL 理论基础 - 二维矩阵_第3张图片

你可能会想“这样做有什么意义?”,好吧,假设我们要进行平移,平移的量为 tx 和 ty ,然后定义一个这样的矩阵

WebGL 理论基础 - 二维矩阵_第4张图片

如果你还记得线性代数的知识,我们可以删除和 0 相乘的部分,和 1 相乘相当于没变,所以简化后为

WebGL 理论基础 - 二维矩阵_第5张图片

或者更简洁

其他的就不用关心了。这个看起来和平移例子中的代码有些相似。

同样的来实现旋转,在旋转章节提到过,旋转只需要和旋转角对应的正弦和余弦值

然后我们创建一个这样的矩阵

WebGL 理论基础 - 二维矩阵_第6张图片

使用矩阵后得到

WebGL 理论基础 - 二维矩阵_第7张图片

遮住没有意义的部分(和 0 或 1 相乘的部分)

WebGL 理论基础 - 二维矩阵_第8张图片

然后得到简化版

正是我们在旋转例子中得到的结果。

最后是缩放,我们将两个缩放因子叫做 sx 和 sy 。

然后创建一个这样的矩阵

WebGL 理论基础 - 二维矩阵_第9张图片

使用矩阵后得到

WebGL 理论基础 - 二维矩阵_第10张图片

实际上是

WebGL 理论基础 - 二维矩阵_第11张图片

简化后

和缩放例子相似。

现在你可能还会想“那又怎样,有什么意义?”,好像花了更多精力做之前做过的事情。

现在开始有趣的部分了,相乘后他们可以用一个矩阵代表三个变换,假定有一个方法m3.multiply可以将两个矩阵相乘并返回结果。

为了方便讲解,我们先创建平移,旋转和缩放矩阵。

var m3 = {
  translation: function(tx, ty) {
    return [
      1, 0, 0,
      0, 1, 0,
      tx, ty, 1,
    ];
  },

  rotation: function(angleInRadians) {
    var c = Math.cos(angleInRadians);
    var s = Math.sin(angleInRadians);
    return [
      c,-s, 0,
      s, c, 0,
      0, 0, 1,
    ];
  },

  scaling: function(sx, sy) {
    return [
      sx, 0, 0,
      0, sy, 0,
      0, 0, 1,
    ];
  },
};

现在该修改着色器了,原来的着色器像这样


在 JavaScript 中需要乘上投影矩阵

// 绘制场景
function drawScene() {
...

// 计算矩阵
var projectionMatrix = m3.projection(
    gl.canvas.clientWidth, gl.canvas.clientHeight);

...

// 矩阵相乘
var matrix = m3.multiply(projectionMatrix, translationMatrix);
matrix = m3.multiply(matrix, rotationMatrix);
matrix = m3.multiply(matrix, scaleMatrix);

...
}

这里还去除了设置分辨率的代码,通过使用矩阵,我们就把着色器中 6-7 步的操作在一步中完成。

WebGL 理论基础 - 二维矩阵_第15张图片

CodePen 地址

在继续之前我们可以先简化一下操作,虽然先创建一些矩阵再将它们相乘很常见,但是按照我们的顺序依次操作矩阵也比较常见,比较高效的做法是创建这样的方法

var m3 = {

  ...

  translate: function(m, tx, ty) {
    return m3.multiply(m, m3.translation(tx, ty));
  },

  rotate: function(m, angleInRadians) {
    return m3.multiply(m, m3.rotation(angleInRadians));
  },

  scale: function(m, sx, sy) {
    return m3.multiply(m, m3.scaling(sx, sy));
  },

  ...

};

这能够让我们将 7 行的矩阵代码转换成 4 行

// 计算矩阵
var matrix = m3.projection(gl.canvas.clientWidth, gl.canvas.clientHeight);
matrix = m3.translate(matrix, translation[0], translation[1]);
matrix = m3.rotate(matrix, angleInRadians);
matrix = m3.scale(matrix, scale[0], scale[1]);

这是结果

WebGL 理论基础 - 二维矩阵_第16张图片

CodePen 地址

最后一件事,我们之前使用了多种矩阵顺序。第一例中使用

translation * rotation * scale // 平移 * 旋转 * 缩放

第二例中使用

scale * rotation * translation // 缩放 * 旋转 * 平移

然后观察了它们的区别。

这有两种方式解读矩阵运算,给定这样一个表达式

projectionMat * translationMat * rotationMat * scaleMat * position

第一种可能是多数人觉得比较自然的方式,从右向左解释

首先将位置乘以缩放矩阵获得缩放后的位置

scaledPosition = scaleMat * position

然后将缩放后的位置和旋转矩阵相乘得到缩放旋转位置

rotatedScaledPosition = rotationMat * scaledPosition

然后将缩放旋转位置和平移矩阵相乘得到缩放旋转平移位置

translatedRotatedScaledPosition = translationMat * rotatedScaledPosition

最后和投影矩阵相乘得到裁剪空间中的坐标

clipspacePosition = projectioMatrix * translatedRotatedScaledPosition

第二种方式是从左往右解释,在这个例子中每一个矩阵改变的都是画布的坐标空间,画布的起始空间是裁剪空间的范围(-1 到 +1),矩阵从左到右改变着画布所在的空间。

第一步:没有矩阵(或者单位矩阵)

WebGL 理论基础 - 二维矩阵_第17张图片

白色区域是画布,蓝色是画布以外,我们正在裁剪空间中。传递到这里的点需要在裁剪空间内。

第二步:matrix = m3.projection(gl.canvas.clientWidth, gl.canvas.clientHeight);

WebGL 理论基础 - 二维矩阵_第18张图片

现在我们在像素空间,X 范围是 0 到 400 ,Y 范围是 0 到 300,0,0 点在左上角。传递到这里的点需要在像素空间内,你看到的闪烁是 Y 轴上下颠倒的原因。

第三步:matrix = m3.translate(matrix, tx, ty);

WebGL 理论基础 - 二维矩阵_第19张图片

原点被移动到了 tx, ty (150, 100),所以空间移动了。

第四步:matrix = m3.rotate(matrix, rotationInRadians);

WebGL 理论基础 - 二维矩阵_第20张图片

空间绕 tx, ty 旋转

第五步:matrix = m3.scale(matrix, sx, sy);

WebGL 理论基础 - 二维矩阵_第21张图片

之前的旋转空间中心在 tx, ty ,x 方向缩放 2 ,y 方向缩放 1.5

在着色器中执行 gl_Position = matrix * position; ,position 被直接转换到这个空间。

选一个你容易接受的方式去理解吧。

另外,如果你想精通矩阵运算就看看这个神奇的视频吧!线性代数的本质:(https://www.bilibili.com/video/av5987715?from=search&seid=3000802315191985263)

你可能感兴趣的:(webgl,计算机视觉,网络,shader,js)