WebGL 实践篇(四)二维矩阵

一 将三个变换集合成为一个矩阵

平移、旋转、缩放的变换还受先后顺序的影响,执行顺序不同,则得到的结果也不同。

用一个矩阵来代表三个变换(原理就不放在实践篇了,可自行扒原理)

    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
        ]
      },
      multiply: function (a, b) {
        var a00 = a[0 * 3 + 0];
        var a01 = a[0 * 3 + 1];
        var a02 = a[0 * 3 + 2];
        var a10 = a[1 * 3 + 0];
        var a11 = a[1 * 3 + 1];
        var a12 = a[1 * 3 + 2];
        var a20 = a[2 * 3 + 0];
        var a21 = a[2 * 3 + 1];
        var a22 = a[2 * 3 + 2];
        var b00 = b[0 * 3 + 0];
        var b01 = b[0 * 3 + 1];
        var b02 = b[0 * 3 + 2];
        var b10 = b[1 * 3 + 0];
        var b11 = b[1 * 3 + 1];
        var b12 = b[1 * 3 + 2];
        var b20 = b[2 * 3 + 0];
        var b21 = b[2 * 3 + 1];
        var b22 = b[2 * 3 + 2];
        return [
          b00 * a00 + b01 * a10 + b02 * a20,
          b00 * a01 + b01 * a11 + b02 * a21,
          b00 * a02 + b01 * a12 + b02 * a22,
          b10 * a00 + b11 * a10 + b12 * a20,
          b10 * a01 + b11 * a11 + b12 * a21,
          b10 * a02 + b11 * a12 + b12 * a22,
          b20 * a00 + b21 * a10 + b22 * a20,
          b20 * a01 + b21 * a11 + b22 * a21,
          b20 * a02 + b21 * a12 + b22 * a22,
        ];
      },
    }

当变换都集合在一个矩阵里,那么着色器的代码就变成了这样:

  <script id="vertex-shader" type="x-shader/x-vertex">
    attribute vec2 a_position;

    uniform vec2 u_resolution;
    uniform mat3 u_matrix;

    void main(){
      vec2 position = (u_matrix * vec3(a_position,1)).xy;

      vec2 zeroToOne = position / u_resolution;

      vec2 zeroToTwo = zeroToOne * 2.0;

      vec2 clipSpace = zeroToTwo - 1.0;

      gl_Position = vec4(clipSpace * vec2(1,-1),0,1);
    }
  </script>

设置矩阵变换的参数:

      var translationMatrix = m3.translation(translations[0], translations[1]);
      var rotationMatrix = m3.rotation(angleInRadians);
      var scaleMatrix = m3.scaling(scale[0], scale[1]);

      var matrix = m3.multiply(translationMatrix, rotationMatrix);
      matrix = m3.multiply(matrix, scaleMatrix);

      webgl.uniformMatrix3fv(matrixUniformLocation, false, matrix);

矩阵变换与之前分别做的三个变换的最终结果是相同的。

如果要改变变换顺序(上边那个是先缩放再旋转再平移的),那么只需要改变一下矩阵运算即可(下边这个是先平移再旋转再缩放),用滑块呈现的不同效果就是,上边的旋转中心在F的左上角,那下边这个旋转中心就是在F外的某一点(大概是[100,150]),缩放中心也如此,平移倒是没有像旋转跟缩放那么大的变化,:

      var matrix = m3.multiply(scaleMatrix, rotationMatrix);
      matrix = m3.multiply(matrix, translationMatrix);

二 改变“F”的原点

按照原来的变换顺序,F是绕着它的左上角进行旋转和缩放的,而我们可以通过矩阵变换来改变旋转的中心

      //将原点移动到“F”中心
      var moveOriginMatrix = m3.translation(-50, -75);
      var matrix = m3.multiply(translationMatrix, rotationMatrix);
      matrix = m3.multiply(matrix, scaleMatrix);
      matrix = m3.multiply(matrix, moveOriginMatrix);

三 投影矩阵

也就是,简化屏幕像素坐标转换成裁剪空间坐标的着色器代码。

首先我们看一下转换的步骤:

1.屏幕像素坐标转换到 0-1;(缩放)

2.再将0-1转换成0-2;(缩放)

3.最后转换成裁剪空间坐标 [-1,1];(平移)

4.翻转Y轴。(缩放)

即,假设一个屏幕坐标为(x,y)的点P,要将其转换成为裁剪空间坐标,即需要:

1. x / u_resolution.x , y / u_resolution.y

2. (x / u_resolution.x) * 2 , (y / u_resolution.y) * 2

3. (x / u_resolution.x) * 2 - 1 , (y / u_resolution.y) * 2 - 1

4. (x / u_resolution.x) * 2 - 1 , -(y / u_resolution.y) * 2 + 1

因此,构成的投影矩阵如下:

    var m3 = {
      projection: function (width, height) {
        return [
          2 / width, 0, 0,
          0, -2 / height, 0,
          -1, 1, 1
        ]
      },
      ...
    }

简化顶点着色器:

  <script id="vertex-shader" type="x-shader/x-vertex">
    attribute vec2 a_position;

    uniform mat3 u_matrix;

    void main(){

      gl_Position = vec4(u_matrix * vec3(a_position,1).xy,0,1);
    }
  </script>

设置矩阵变换:

      var projectionMatrix = m3.projection(webgl.canvas.clientWidth, webgl.canvas.clientHeight);

      //执行顺序一般从后往前
      var matrix = m3.multiply(projectionMatrix, translationMatrix);
      matrix = m3.multiply(matrix, rotationMatrix);
      matrix = m3.multiply(matrix, scaleMatrix);

四 层次变换

构造五个“F”,每一个F相对于前一个F进行变换。

1.构造五个“F”,那就是要对“F”进行复制,就要用到单位矩阵

    var m3 = {
      identity: function () {
        return [
          1, 0, 0,
          0, 1, 0,
          0, 0, 1
        ]
      },

 2.后一个“F”是基于前一个F做变换:

      var matrix = m3.identity();
      matrix = m3.multiply(matrix, projectionMatrix);

      for (var i = 0; i < 5; i++) {
        matrix = m3.multiply(matrix, translationMatrix);
        matrix = m3.multiply(matrix, rotationMatrix);
        matrix = m3.multiply(matrix, scaleMatrix);
        webgl.uniformMatrix3fv(matrixUniformLocation, false, matrix);

        webgl.drawArrays(webgl.TRIANGLES, 0, 18)
      }

 结果如下:

 PS:旋转时旋转中心在第一个F的左上角。平移跟缩放应该都是基于前一个F进行的变换。

五 执行顺序的两种解释(自我理解,详细的还是扒原理吧)

1. 从后往前的解释:以物体为中心,物体在进行矩阵变换

2. 从前往后的解释:以画布(坐标)为中心,是画布在进行矩阵变换,类似于你在一个框定的视口内(手机屏幕)玩手机里的某个图形(平移,放大)

六  clientWidth, clientHeight以及width,height

大多数情况下,有关于画布的大小使用canvas.width和canvas.height;但要计算长宽比时一般利用canvas.clientWidth和canvas.clientHeight,比如上述投影矩阵projection中传递进去的参数。

七 矩阵变换库

不管二维三维,矩阵变换一般都可引入外部库进行函数的调用,只用了解它们的调用方式就好了,在使用过程中也要注意它们的执行顺序。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值