webgl变换:深入图形平移

更新日期: 2021-03-13阅读: 1.3k标签: WebGL

在以前的文章里,不管是绘制图形,绘制点亦或者是改变色值,所有的内容都是静态的。

在 webgl 里,图形的运动分为 平移、旋转、缩放 三种类型。

接下来,我们会从零基础开始,一点一点来深入了解图形如何进行运动。

首先来从零开始了解下图形的平移


1. 图形平移

首先我们来看如何实现图形的平移操作。

平移的操作就是将图形的原始坐标加上对应的移动距离。首先来看下平移的实现

const vertexShaderSource = "" +
      "attribute vec4 apos;" + // 定义一个坐标
      "uniform float x;" + // 处理 x 轴移动
      "uniform float y;" + // 处理 y 轴移动
      "void main(){" +
      " gl_Position.x = apos.x + x;" +
      " gl_Position.y = apos.y + y;" +
      " gl_Position.z = 0.0;" + // z轴固定
      " gl_Position.w = 1.0;" +
      "}";
const fragmentShaderSource = "" +
      "void main(){" +
      " gl_FragColor = vec4(1.0,0.0,0.0,1.0);" +
      "}";

// initShader已经实现了很多次,本次就不再赘述了
const program = initShader(gl,vertexShaderSource,fragmentShaderSource);

const buffer = gl.createBuffer();
const data = new Float32Array([
  0.0,0.0,
  -0.5,-0.5,
  0.5,-0.5,
]);

gl.bindBuffer(gl.ARRAY_BUFFER,buffer);
gl.bufferData(gl.ARRAY_BUFFER,data,gl.STATIC_DRAW);

const aposlocation = gl.getAttribLocation(program,'apos');
const xlocation = gl.getUniformLocation(program,'x');
const ylocation = gl.getUniformLocation(program,'y');

gl.vertexAttribPointer(aposlocation,2,gl.FLOAT,false,0,0);
gl.enableVertexAttribArray(aposlocation);

let x = 0.0;
let y = 0.0;
function run () {
  gl.uniform1f(xlocation,x += 0.01);
  gl.uniform1f(ylocation,y += 0.01);

  gl.drawArrays(gl.TRIANGLES,0,3);
  // 使用此方法实现一个动画
  requestAnimationFrame(run)
}
run()

解释:

  • 首先声明一个变量 x 和变量 y ,用来处理 x轴 和 y轴 的坐标。这里使用的是 uniform 变量,因为平移的操作对于图形上的所有顶点都有影响。
  • 通过 gl_Position.[xyzw] 来分别设置 x、y、z、w 的值。用于改变图形位置。
  • 使用 gl.uniform1f 来为 x 和 y 赋值
  • 使用 requestAnimationFrame 实现一个缓动动画。方便观察效果。
  • 其他的操作,缓冲区,绘制,赋值,激活,

可以看到,这样处理图形移动的话很好理解,但是因为一个移动,我们声明了两个 uniform 变量来实现。并且分开设置的 xyz 坐标,非常的不方便。

所以,在处理webgl变换(平移、缩放、旋转)的时候,通常使用矩阵来实现。接下来就来看看,如何使用矩阵实现图形的平移。


2. 平移矩阵

推导平移矩阵的步骤:

  • 获取平移前后的图形坐标(三维)
  • 计算平移前后的差值
  • 带入到平移矩阵
  • 处理图形顶点
  • 获得平移后的图形

2.1 平移矩阵的推导

首先让我们来看一幅图片。


这幅图片的意义就是我们将橙色的三角形移动到蓝色虚线三角形处。

移动之后的蓝色虚线三角形的三个坐标分别为

  • x’ = x + x1
  • y' = y + y1
  • z' = z + z1
  • w=1 齐次坐标为1

2.2 获得平移矩阵

在 webgl 中,通常使用矩阵来实现图形变换。下面我们来看看矩阵如何表示。


左侧是平移之前的原始坐标,中间的是一个平移矩阵,经过两者相乘,可以得到一个平移之后的坐标。

现在我们来看下平移矩阵如何计算得出

首先通过上述图片中的矩阵我们来得到几个方程式。用左侧的列分别乘矩阵的行,可以得到一下公式

  • ax + by + cz + w = x'
  • ex + fy + gz + h = y'
  • ix + jy + kz + l = z'
  • mx + ny + oz + p = w'

公式合并:

第一节 里的四个方程式和第二节里的四个方程式合并,可以得到如下结果:

  • ax + by + cz + w = x + x1':只有当 a = 1,b = c = 0, w = x1 的时候,等式左右两边成立
  • ex + fy + gz + h = y + y1':只有当 f = 1, e = g = 0, h = y1 的时候,等式左右两边成立
  • ix + jy + kz + l = z + z1':只有当 k = 1,i = j = 0, l = z1 的时候,等式左右两边成立
  • mx + ny + oz + p = 1':只有当 m = n = o = 0, p = 1 的时候,等式左右两边成立

经过上述方程式,可以得到一个平移的矩阵:

| 1 0 0 x |

| 0 1 0 y |

| 0 0 1 z |

| 0 0 0 1 |

之后将平移矩阵和原始坐标相乘,就可以得到平移之后的坐标。


3. 矩阵实战

来看看使用矩阵如何处理图形的平移。

第一步,创建着色器源代码
const vertexShaderSource = "" +
      "attribute vec4 apos;" +
      "uniform mat4 mat;" + // 创建一个 uniform 变量,代表平移矩阵
      "void main(){" +
      " gl_Position = mat * apos;" + // 矩阵与原始坐标相乘
      "}";
const fragmentShaderSource = "" +
      "void main(){" +
      " gl_FragColor = vec4(1.0,0.0,0.0,1.0);" +
      "}";
第二步,创建平移矩阵
let Tx = 0.1;    //x坐标的位置
let Ty = 0.1;    //y坐标的位置
let Tz = 0.0;    //z坐标的位置
let Tw = 1.0;    //差值
const mat = 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,
  Tx,Ty,Tz,Tw,
]);

这里可以看到,使用的矩阵和我们推导出来的矩阵不太一样,推导的平移矩阵里 xyzw 位于矩阵的右侧,现在是位于矩阵的底部,这是为什么呢?

这是因为在 webgl 中,矩阵的使用需要按照 左上右下 的对角线做一次翻转。所以使用的矩阵,xyzw 位于底部

第三步,绘制一个三角形
const program = initShader(gl,vertexShaderSource,fragmentShaderSource);
const aposlocation = gl.getAttribLocation(program,'apos');
const data =  new Float32Array([
  0.0,0.0,
  -.3,-.3,
  .3,-.3
]);

const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER,buffer);
gl.bufferData(gl.ARRAY_BUFFER,data,gl.STATIC_DRAW);

gl.vertexAttribPointer(aposlocation,2,gl.FLOAT,false,0,0);
gl.enableVertexAttribArray(aposlocation);

gl.drawArrays(gl.TRIANGLES,0,3); // 第五步的时候会重写
第四步,获取矩阵变量,给矩阵赋值
const matlocation = gl.getUniformLocation(program,'mat');
gl.uniformMatrix4fv(matlocation,false,mat);

这里使用 gl.uniformMatrix4fv 来给矩阵赋值。

第五步,添加缓动动画
function run () {
  Tx += 0.01
  Ty += 0.01
  const mat = 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,
    Tx,Ty,Tz,Tw,
  ]);
  gl.uniformMatrix4fv(matlocation,false,mat);
  gl.drawArrays(gl.TRIANGLES,0,3);

  // 使用此方法实现一个动画
  requestAnimationFrame(run)
}
run()


4. 完整代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<canvas id="webgl" width="500" height="500"></canvas>
<script>
  const gl = document.getElementById('webgl').getContext('webgl');
  const vertexShaderSource = "" +
    "attribute vec4 apos;" +
    "uniform mat4 mat;" +
    "void main(){" +
    " gl_Position = mat * apos;" +
    "}";
  const fragmentShaderSource = "" +
    "void main(){" +
    " gl_FragColor = vec4(1.0,0.0,0.0,1.0);" +
    "}";

  const program = initShader(gl,vertexShaderSource,fragmentShaderSource);
  const aposlocation = gl.getAttribLocation(program,'apos');
  const matlocation = gl.getUniformLocation(program,'mat');

  const data =  new Float32Array([
    0.0,0.0,
    -.3,-.3,
    .3,-.3
  ]);
  const buffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER,buffer);
  gl.bufferData(gl.ARRAY_BUFFER,data,gl.STATIC_DRAW);

  gl.vertexAttribPointer(aposlocation,2,gl.FLOAT,false,0,0);
  gl.enableVertexAttribArray(aposlocation);

  let Tx = 0.1;    //x坐标的位置
  let Ty = 0.1;    //y坐标的位置
  let Tz = 0.0;    //z坐标的位置
  let Tw = 1.0;    //差值
  function run () {
    Tx += 0.01
    Ty += 0.01
    const mat = 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,
      Tx,Ty,Tz,Tw,
    ]);
    gl.uniformMatrix4fv(matlocation,false,mat);
    gl.drawArrays(gl.TRIANGLES,0,3);

    // 使用此方法实现一个动画
    requestAnimationFrame(run)
  }
  run()
  function initShader(gl,vertexShaderSource,fragmentShaderSource){
    const vertexShader = gl.createShader(gl.VERTEX_SHADER);
    const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);

    gl.shaderSource(vertexShader,vertexShaderSource);
    gl.shaderSource(fragmentShader,fragmentShaderSource);

    gl.compileShader(vertexShader);
    gl.compileShader(fragmentShader);

    const program = gl.createProgram();

    gl.attachShader(program,vertexShader);
    gl.attachShader(program,fragmentShader)

    gl.linkProgram(program);
    gl.useProgram(program);
    return program;
  }
</script>
</body>
</html>

至此,通过矩阵控制图形移动就全部实现完成了。


链接: https://www.fly63.com/article/detial/10417

玩转HTML5 下 WebGL 的 3D 模型交并补

建设性的立体几何具有许多实际用途,它用于需要简单几何对象的情况下,或者数学精度很重要的地方,几乎所有的工程 CAD 软件包都使用 CSG(可以用于表示刀具切削,以及零件必须配合在一起的特征)。

ht.js基于 HTML5 WebGL 的 3D “弹力”布局

使用弹力布局功能需要在引入 ht.js 核心库之后,再引入一个 ht-forcelayout.js 的弹力布局插件库,因为还用到了 form 表单,所以要引入 ht-form.js 的表单插件库。

基于 HTML5 WebGL 的 3D 网络拓扑结构图

前端开发者觉得如果我们可以不用学习 unity3d 或者其他游戏开发工具就能实现 3D 效果,而且能够精准的靠代码来控制移动或者方向就好了,于是我利用 HT For Web 中的3D组件 来实现了一个小例子

快速开发 HTML5 WebGL 的 3D 斜面拖拽生成模型

3D 场景中的面不只有水平面这一个,空间是由无数个面组成的,所以我们有可能会在任意一个面上放置物体,而空间中的面如何确定呢?我们知道,空间中的面可以由一个点和一条法线组成。这个 Demo 左侧为面板,从面板中拖动物体到右侧的 3D 场景中

WebGL的3D框架比较 Three.js和ThingJS

随着flash的没落,浏览器的原生能力的兴起。在3D方面WebGL不管从功能还是性能方面都在逐渐加强。2D应用变为3D应用的需求也越来越强烈。 win10的画图板支持3D图片,2d工具photoshop也开始逐步集成了3D工具。

WebGL 纹理颜色原理

WebGL绘制图像时,往着色器中传入颜色信息就可以给图形绘制出相应的颜色,现在已经知道顶点着色器和片段着色器一起决定着向颜色缓冲区写入颜色信息并最终呈现出来,那么这个过程是什么样

使用 WebGL 进行实时视频图像处理

这是我最近在 CodePen 上制作的 WebGL 演示案例。它可以捕获网络摄像头的数据(或在无法访问网络摄像头时,从 placekitten 获取备用图像),并将其实时转换为 ASCII 图像艺术。

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!