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 = https://tazarkount.com/read/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 平移矩阵的推导首先让我们来看一幅图片 。
【webgl变换:深入图形平移】
webgl变换:深入图形平移

文章插图
这幅图片的意义就是我们将橙色的三角形移动到蓝色虚线三角形处 。
移动之后的蓝色虚线三角形的三个坐标分别为
  • x’ = x + x1
  • y' = y + y1
  • z' = z + z1
  • w=1 齐次坐标为1
2.2 获得平移矩阵在 webgl 中 , 通常使用矩阵来实现图形变换 。下面我们来看看矩阵如何表示 。
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 =https://tazarkount.com/read/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.01Ty += 0.01const 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 =https://tazarkount.com/read/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.01Ty += 0.01const 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;}至此 , 通过矩阵控制图形移动就全部实现完成了 。
今天的分享就到这儿了 , 
Bye~
webgl变换:深入图形平移

文章插图
更多精彩内容 , 定制礼品图书赠送 , 高薪职位内推 , 微信搜索关注“豆皮范儿”