开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第4天,点击查看活动详情
单调的图形总是枯燥无味接下来讲讲如何让图形动起来,想要图形动起来就先要了解怎么实现图形的变换,变换一般有三种,平移、旋转和缩放。
平移
先从简单的平移开始:只需要每个顶点加上偏移量就可以实现图形的偏移,所以只需要修改顶掉着色器,每个顶点都是相同的位移所以可以使用uniform变量。具体位移公式如下
- x' = x + T.x;
- y' = y + T.y;
- z' = z + T.z;
const VSHADER_SOURCE = `
attribute vec4 a_Position;
uniform vec4 u_Translate;
void main() {
gl_Position = u_Translate + a_Position;
}
`;
const u_Translate = gl.getUniformLocation(gl.program, 'u_Translate')
gl.uniform4f(u_Translate, 0.5, 0.5, 0.0, 0);
其中u_Translate + a_Position为每一个分量相加得到一个新的vec4值。
旋转
对于旋转可能会复杂一些,假设我们旋转角度为B,绕z轴旋转,计算过程就省略了,通过计算后会得到如下公式:
- x' = x cosB - y sinB
- y' = x sinB + y cosB
- z' = z
const VSHADER_SOURCE = `
attribute vec4 a_Position;
uniform float u_CosB;
uniform float u_SinB;
void main() {
gl_Position.x = a_Position.x * u_CosB - a_Position.y * u_SinB;
gl_Position.y = a_Position.x * u_SinB + a_Position.y * u_CosB;
gl_Position.z = a_Position.z;
gl_Position.w = 1.0;
}
`;
但从着色器上就可以看到非常的复杂了,如果再去实现组合变换就非常麻烦,所以我们需要使用到一个数学工具变换矩阵。
变换矩阵
为了得到平移的矩阵我们先来看看平移的矩阵计算:
目标位置 = 平移矩阵 X 原始位置
通过矩阵计算得到
- x' = ax + by + cz + d
- y' = ex + fy + gz + h
- z' = ix + jy + kz + l
- 1 = mx + ny + oz + p
因为平移只需要在之前的基础上加上新的位移所以得出平移矩阵为:
同理可以得到旋转矩阵为:
缩放矩阵为:
有了这些矩阵后可以直接传递矩阵给着色器,在着色器中做矩阵的叉乘就可以得到变换后的图形了。在WebGL中我们会把这类变换矩阵称之为模型矩阵(model matrix) code
// 顶点着色器
const VSHADER_SOURCE = `
attribute vec4 a_Position;
// 4x4矩阵
uniform mat4 u_xformMatrix;
void main() {
// 矩阵乘法计算
gl_Position = u_xformMatrix * a_Position;
}
`
// 弧度制
const radian = Math.PI / 4
const cosB = Math.cos(radian)
const sinB = Math.sin(radian)
// 旋转矩阵
const xformMatrix = new Float32Array([
cosB, sinB, 0.0, 0.0,
-sinB, cosB, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0
])
// 旋转矩阵传输给着色器
const u_xformMatrix = gl.getUniformLocation(gl.program, 'u_xformMatrix')
gl.uniformMatrix4fv(u_xformMatrix, false, xformMatrix)
在顶点着色器中mat4类型是glsl的4x4矩阵,同时使用*运算符就可以进行矩阵与矢量的叉乘运算。
通过计算出来的正弦余弦值可以补充旋转矩阵,仔细的你会发现旋转矩阵的值的位置好像有猫腻,矩阵有两种储存方式,一种是按行主序一种是按列主序,在WebGL和OpenGL中规定矩阵都是按列主序的。所以传递的数据和我们想象的矩阵长得不太一样。
最后通过uniformMatrix4fv传递数据到着色器。其实还可以再偷懒一点,使用现成的js库来传入位移和旋转角度生成各种矩阵,因为是单纯api使用这里就不赘述了,后续有用到会注释说明。
动画
图形能够变化了,就可以讲讲怎么动起来了。code
// 通过库函数创建矩阵对象
const modelMatrix = new Matrix4()
// 设置初始化旋转角度的矩阵
modelMatrix.setRotate(1, 1, 1, 1)
const u_ModelMatrix = gl.getUniformLocation(gl.program, 'u_ModelMatrix')
const tick = () => {
// 乘以旋转角度
modelMatrix.rotate(1, 1, 1, 1)
// 传递新的旋转矩阵
gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements)
// 绘制前需要清空颜色缓冲区
gl.clear(gl.COLOR_BUFFER_BIT)
// 绘制方式; 开始顶点; 总顶点数;
gl.drawArrays(gl.TRIANGLE_FAN, 0, 3)
requestAnimationFrame(tick)
}
tick()
去除复杂的矩阵计算后,可以看到动画就跟我们的Canvas动画的步骤基本上一致。