vlambda博客
学习文章列表

webgl变换:深入图形平移

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

1、

2、

3、

在 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 + x1y' = y + y1z' = z + z1w=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 = 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>

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

今天的分享就到这儿了,

Bye~

福利时间

字节跳动9周年徽章2枚

webgl变换:深入图形平移

webgl变换:深入图形平移

所有徽章实物如下图所示:

webgl变换:深入图形平移

心动❤️嘛?看看就心动,赶紧往下看看如何获奖。


那么如何获奖呢?

获奖分为以下3步

抽奖和领取奖品等其他事项:

The     End

如果你觉得这篇文章对你有帮助,有启发,我想请你帮我2个小忙:

1、点个「在看」,让更多的人也能看到这篇文章内容;


1、豆皮范儿后台回复「vis」,还可以获取更多可视化免费学习资料。

2、豆皮范儿后台回复「webgl」,还可以获取webgl免费学习资料。

3、豆皮范儿后台回复「算法」,还可以获取算法的学习资料。

4、豆皮范儿后台回复「招聘」,获取各种内推。


豆皮范儿
小豆皮的前端范儿
54篇原创内容
Official Account