目录

WebGL打开 3D 世界的大门(四):二维变换和矩阵

本篇文章的内容假定你懂的矩阵的基本知识,例如线性变换,其次坐标,平移矩阵等知识。 有了二维变换的知识,我们就可以为三维变化做好准备。好戏开场了,我们先来画一个,字母E如图:

代码地址gitee.com/feng-lianxi...

顶点坐标如下:

markdown 复制代码
    var positions = [
            0, 0,
            300, 0, 
            300, 30,

            0, 0, 
            0, 30,
            300,30,

            0,0,
            0,400,
            30,400,

            0,0,
            30,0,
            30,400,

            0,400,
            300,400,
            0,370,

            0,370,
            300,370,
            300,400,

            0,185,
            200,185,
            200,215,

            0,215,
            0,185,
            200,215
        ];
     drawPoints(gl.TRIANGLES,positions,2,24) //画出字母E,
     

接下来有的同学就会想:那不简单了,我们给出变换矩阵,然后修改每个顶点的坐标,重新画一次不就行了!,我只能告诉你,同学醒醒,我们要充分利用gpu的性能,要节省出cpu,让gpu处理矩阵比cpu快的多。所以我们要修改shader,我们通过修改顶点的shader重新绘制,并且GLSL语言本身就支持向量计算。接下来我们实现一个平移动画,我们手把手实现:

平移变换

  1. 修改shader:顶点着色器。添加转换变量。

    ini 复制代码
       // an attribute will receive data from a buffer
       attribute vec2 a_position;
       uniform vec2 u_resolution;
       // attribute vec4 a_position;
       // all shaders have a main function
       varying vec4 v_color;
       uniform vec2 u_translation;
       void main() {
         // gl_Position is a special variable a vertex shader
         // is responsible for setting
         // 从像素坐标转换到 0.0 到 1.0
         vec2 position = a_position + u_translation;
         vec2 zeroToOne = position - u_resolution / 2.0;
    
         // 再把 0->1 转换 0->2
         vec2 zeroToTwo = zeroToOne / u_resolution * 2.0;
    
         // 把 0->2 转换到 -1->+1 (裁剪空间)
         vec2 clipSpace = zeroToTwo * vec2(1.0,-1.0);
    
         gl_Position = vec4(clipSpace, 0, 1);
         gl_PointSize = 10.0;
         v_color = vec4(clipSpace, 0.5, 1);
       }
       
  2. 修改u_translation变量并绘制:

    scss 复制代码
     var translationLocation = gl.getUniformLocation(program, "u_translation");
     gl.uniform2f(resolutionUniformLocation, translation, 0);
     // Set clear color to black, fully opaque
     gl.clearColor(0.0, 0.0, 0.0, 1.0);
     // Clear the color buffer with specified clear color
     gl.clear(gl.COLOR_BUFFER_BIT);
     gl.drawArrays(gl.TRIANGLES, 0, 24);
  3. 添加动画控制:

    ini 复制代码
     function animateTranslate(duration,distance) {
         let start = Date.now();
         function step() {
             console.log('run step;')
             let timePassed = Date.now() - start;
             let progress = timePassed / duration;
             if (progress <= 1) {
                 drawE(distance*progress);
                 window.requestAnimationFrame(step);
             } else {
                 return;
             }
         }
     
         window.requestAnimationFrame(step);
     }
    
     animateTranslate(3000,150);

变换矩阵

我们常见的几个矩阵是:

javascript 复制代码
   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,
           ];
       },
   };
   

如果不太清楚为什么是这样的同学,可以补习一下矩阵变换的相关知识。

由于GLSL语言本身是支持矩阵计算的。所以我们可以继续修改shader。如下:

ini 复制代码
  // an attribute will receive data from a buffer
  attribute vec2 a_position;
  uniform vec2 u_resolution;
  uniform mat3 matrix_translate;
  uniform mat3 matrix_rotate;
  uniform mat3 matrix_scale;

  // attribute vec4 a_position;
  // all shaders have a main function
  varying vec4 v_color;
  //uniform vec2 u_translation;
  void main() {
    // gl_Position is a special variable a vertex shader
    // is responsible for setting
    // 从像素坐标转换到 0.0 到 1.0
    // vec2 position = a_position + u_translation;
    vec2 position = (matrix_translate * matrix_rotate * matrix_scale * vec3(a_position, 1)).xy;
    vec2 zeroToOne = position - u_resolution / 2.0;

    // 再把 0->1 转换 0->2
    vec2 zeroToTwo = zeroToOne / u_resolution * 2.0;

    // 把 0->2 转换到 -1->+1 (裁剪空间)
    vec2 clipSpace = zeroToTwo * vec2(1.0,-1.0);

    gl_Position = vec4(clipSpace, 0, 1);
    gl_PointSize = 10.0;
    v_color = vec4(clipSpace, 0.5, 1);
  }

然后就可以对全局变量赋值,进行旋转平移和缩放了。

ini 复制代码
    var translateUniform = gl.getUniformLocation(program, "matrix_translate");
    gl.uniformMatrix3fv(translateUniform, false, m3.translation(100,200));
    
    var rotateUniformn = gl.getUniformLocation(program, "matrix_rotate");
    gl.uniformMatrix3fv(rotateUniformn, false, m3.rotation(1));

    var scaleUniform = gl.getUniformLocation(program, "matrix_scale");
    gl.uniformMatrix3fv(scaleUniform, false, m3.scaling(0.5,0.5));

    gl.drawArrays(gl.TRIANGLES, 0, 24);
    

效果如下:

关于矩阵

有一点需要注意glsl里面的矩阵和向量和数学里面的矩阵向量结构是不相同的。 在数学里面,矩阵的列是向量,但是在编码里面,列是向量不太符合我们的编码习惯,在glsl里面行是向量,其实就是数学矩阵里面的转置矩阵,参考平移矩阵,这是由于历史原因,向量的数字在内存里面是连续的。

总结

我们已经介绍了基本作图和矩阵变换,接下来就让我们一起进入3d的世界吧,我们已经做好了充分的准备。我们来一起了解透视基本原理,下节更加精彩。

本文是转载文章,点击查看原文
如有侵权,请联系 xyy@jishuzhan.net 删除
相关推荐
艾克马斯奎普特1 分钟前
Vue.js 3 渐进式实现之响应式系统——第六节:嵌套的 effect 与 effect 栈
前端·vue.js
Nickname肖知寒3 分钟前
chromium魔改——CDP(Chrome DevTools Protocol)检测01
前端·chrome·chrome devtools
FanetheDivine7 分钟前
发现一个Solid中的坑
前端·javascript·react.js
Cache技术分享18 分钟前
40. Java 使用 `switch` 语句进行分支选择
前端·后端
zzkrix24 分钟前
浏览器插件 - kimi 历史会话清理助手
前端
前端开发张小七28 分钟前
11.Python设计模式:单例模式与工厂模式实战指南
前端·python
yanyu-yaya36 分钟前
第四章 react-redux,@reduxjs/toolkit依赖,学习
前端·学习·react.js
小付同学呀41 分钟前
前端快速入门学习3——CSS介绍与选择器
前端·css·学习
Jenlybein42 分钟前
快速了解浏览器原理及工作流程
前端·浏览器
醋醋42 分钟前
vue2源码记录(2)
前端·vue.js