五、图形变换
1.图形平移-着色器
js
const canvas = document.querySelector("#canvas");
const gl = canvas.getContext("webgl");
// 顶点着色器
const VERTEX_SHADER_SOURCE = `
attribute vec4 aPosition;
attribute float aTranslate;
void main() {
gl_Position = vec4(aPosition.x+aTranslate,aPosition.y,aPosition.z,1.0);
}
`;
const FRAGMENT_SHADER_SOURCE = `
void main() {
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}
`;
const program = initShader(
gl,
VERTEX_SHADER_SOURCE,
FRAGMENT_SHADER_SOURCE
);
const aPosition = gl.getAttribLocation(program, "aPosition");
const aTranslate = gl.getAttribLocation(program, "aTranslate");
const points = new Float32Array([
-0.5, -0.5,
0.5, -0.5,
-0.5, 0.5,
]);
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);
gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false,0, 0);
gl.enableVertexAttribArray(aPosition);
let x = 0;
const animate = () => {
requestAnimationFrame(animate);
x += 0.01;
gl.vertexAttrib1f(aTranslate, x);
gl.drawArrays(gl.TRIANGLE_FAN, 0, 3);
};
animate();2.图形缩放-着色器
js
const canvas = document.querySelector("#canvas");
const gl = canvas.getContext("webgl");
// 顶点着色器
const VERTEX_SHADER_SOURCE = `
attribute vec4 aPosition;
attribute float aScale;
void main() {
gl_Position = vec4(aPosition.x*aScale,aPosition.y,aPosition.z,1.0);
}
`;
const FRAGMENT_SHADER_SOURCE = `
void main() {
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}
`;
const program = initShader(
gl,
VERTEX_SHADER_SOURCE,
FRAGMENT_SHADER_SOURCE
);
const aPosition = gl.getAttribLocation(program, "aPosition");
const aScale = gl.getAttribLocation(program, "aScale");
const points = new Float32Array([
-0.5, -0.5,
0.5, -0.5,
-0.5, 0.5,
]);
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);
gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(aPosition);
let x = 0;
const animate = () => {
requestAnimationFrame(animate);
x += 0.01;
gl.vertexAttrib1f(aScale, x);
gl.drawArrays(gl.TRIANGLE_FAN, 0, 3);
};
animate();3.图形旋转-着色器
js
const canvas = document.querySelector("#canvas");
const gl = canvas.getContext("webgl");
// 顶点着色器
const VERTEX_SHADER_SOURCE = `
attribute vec4 aPosition;
attribute float deg;
void main() {
gl_Position.x = aPosition.x*cos(deg)-aPosition.y*sin(deg);
gl_Position.y = aPosition.x*sin(deg)+aPosition.y*cos(deg);
gl_Position.z = aPosition.z;
gl_Position.w = aPosition.w;
}
`;
const FRAGMENT_SHADER_SOURCE = `
void main() {
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}
`;
const program = initShader(
gl,
VERTEX_SHADER_SOURCE,
FRAGMENT_SHADER_SOURCE
);
const aPosition = gl.getAttribLocation(program, "aPosition");
const deg = gl.getAttribLocation(program, "deg");
const points = new Float32Array([
-0.5, -0.5,
0.5, -0.5,
-0.5, 0.5,
]);
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);
gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(aPosition);
let x = 0;
const animate = () => {
requestAnimationFrame(animate);
x += 0.01;
gl.vertexAttrib1f(deg, x);
gl.drawArrays(gl.TRIANGLE_FAN, 0, 3);
};
animate();4.图形平移-矩阵
4.1 线性代数平移知识
1.下图中,当点A移动到A',可以得到下列式子

matlab
// 公式一
x' = x + x1
y' = y + y1
z' = z + z1
w' = w = 1 // 齐次坐标为12.我们定义一个四维矩阵,与位置向量vec4(x,y,z,w)相乘,可以得出下列式子
matlab
// 公式二
ax + by + cz + d = x'
ex + fy + gz + h = y'
ix + jy + kz + l = z'
mx + ny + oz + p = w'3.由公式一、二可得
matlab
ax + by + cz + d = x + x1 // 仅当a=1,b=c=0,d=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时,等式两边成立4.由此可得系数矩阵
5.通过js将系数矩阵转化为列主序,返回列主序的矩阵数组
js
// 平移矩阵
/**
*
* @param {number} x 平移后的x坐标
* @param {number} y 平移后的y坐标
* @param {number} z 平移后的z坐标
* @returns
*/
const getTranslateMatrix = (x=0, y=0, z=0) => {
return 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,
x , y , z , 1 ,
]);
};4.2 给uniform矩阵变量赋值
通过gl.uniformMatrix4fv()给uniform矩阵变量赋值,语法如下
js
gl.uniformMatrix4fv(location, transpose, array)- location:指定 uniform 变量的存储位置
- transpose:在 webgl 中恒为false
- array:矩阵数组
4.3 实现图形平移
js
const canvas = document.querySelector("#canvas");
const gl = canvas.getContext("webgl");
// 顶点着色器
const VERTEX_SHADER_SOURCE = `
attribute vec4 aPosition;
uniform mat4 mat; // 创建四维矩阵
void main() {
gl_Position = mat * aPosition; // 四维矩阵与 位置向量 相乘
}
`;
const FRAGMENT_SHADER_SOURCE = `
void main() {
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}
`;
const program = initShader(gl,VERTEX_SHADER_SOURCE,FRAGMENT_SHADER_SOURCE);
const aPosition = gl.getAttribLocation(program, "aPosition");
const mat = gl.getUniformLocation(program, "mat");
const points = new Float32Array([
-0.5, -0.5,
0.5, -0.5,
-0.5, 0.5,
]);
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);
gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false,0, 0);
gl.enableVertexAttribArray(aPosition);
gl.drawArrays(gl.TRIANGLE_FAN, 0, 3);
let x = 0;
// 平移矩阵
const getTranslateMatrix = (x=0, y=0, z=0) => {
return 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,
x , y , z , 1 ,
]);
};
const animate = () => {
x += 0.01;
if (x > 1) x = 0;
const matrix = getTranslateMatrix(x, x);
gl.uniformMatrix4fv(mat, false, matrix);
gl.drawArrays(gl.TRIANGLES, 0, 3);
requestAnimationFrame(animate);
};
animate();5.图形缩放-矩阵
5.1.线性代数缩放知识
1.下图中,当点A移动到A',可以得到公式一

matlab
// 公式一
x' = Tx * x
y' = Ty * y
z' = Tz * z
w' = w = 12.查看点A和点A’之间的映射公式,可得公式二
matlab
// 公式二
ax + by + cz + d = x'
ex + fy + gz + h = y'
ix + jy + kz + l = z'
mx + ny + oz + p = w'3.合并公式一、二得
matlab
ax + by + cz + d = Tx * x // 仅当x=Tx,b=c=d=0时,等式两边成立
ex + fy + gz + h = Ty * y // 仅当f=Ty,e=g=h=0时,等式两边成立
ix + jy + kz + l = Tz * z // 仅当k=Tz,i=j=l=0时,等式两边成立
mx + ny + oz + p = 1 // 仅当m=n=o=0,p=1时,等式两边成立4.由此可得系数矩阵,此矩阵行主序和列主序都相同
5.获取缩放矩阵
js
/**
*
* @param {number} x 缩放后的x坐标
* @param {number} y 缩放后的y坐标
* @param {number} z 缩放后的z坐标
* @returns
*/
const getScaleMatrix = (x=1, y=1, z=1) => {
return new Float32Array([
x,0.0,0.0,0.0,
0.0,y,0.0,0.0,
0.0,0.0,z,0.0,
0.0,0.0,0.0,1,
]);
};5.2 实现图形缩放
js
const canvas = document.querySelector("#canvas");
const gl = canvas.getContext("webgl");
// 顶点着色器
const VERTEX_SHADER_SOURCE = `
attribute vec4 aPosition;
uniform mat4 mat; // 创建四维矩阵
void main() {
gl_Position = mat * aPosition; // 四维矩阵与 位置向量 相乘
}
`;
const FRAGMENT_SHADER_SOURCE = `
void main() {
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}
`;
const program = initShader(
gl,
VERTEX_SHADER_SOURCE,
FRAGMENT_SHADER_SOURCE
);
const aPosition = gl.getAttribLocation(program, "aPosition");
const mat = gl.getUniformLocation(program, "mat");
const points = new Float32Array([-0.5, -0.5, 0.5, -0.5, -0.5, 0.5]);
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);
gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(aPosition);
const getScaleMatrix = (x=1, y=1, z=1) => {
return new Float32Array([
x,0.0,0.0,0.0,
0.0,y,0.0,0.0,
0.0,0.0,z,0.0,
0.0,0.0,0.0,1,
]);
};
let x = 0;
const animate = () => {
x += 0.01;
if (x > 1) x = 0;
const matrix = getTranslateMatrix(x, x);
console.log(matrix);
gl.uniformMatrix4fv(mat, false, matrix);
gl.drawArrays(gl.TRIANGLES, 0, 3);
requestAnimationFrame(animate);
};
animate();6.图形平移-矩阵
6.1 线性代数旋转知识

1.上图中点A旋转到点A'可得
顶点A
matlab
x = R * cos(α)
y = R * sin(α)
z = 0顶点A'
matlab
x' = R * cos(α + β)
= R * (cos(α)*cos(β) - sin(α)*sin(β))
= R * cos(α)*cos(β) - R * sin(α)*sin(β)
y' = R * sin(α + β)
= R * (sin(α)*cos(β) - cos(α)*sin(β))
= R * sin(α)*cos(β) - R * cos(α)*sin(β)公式合并之后得到公式一
matlab
// 公式一
x' = x/cos(α) * cos(α)*cos(β) - y/sin(α) * sin(α)*sin(β)
= x * cos(β) - y * sin(β)
y' = y/sin(α) * sin(α)*cos(β) - x/cos(α) * cos(α)*sin(β)
= y * cos(β) + x * sin(β)
z' = z2.通过矩阵推导
matlab
// 公式二
ax + by + cz + d = x'
ex + fy + gz + h = y'
ix + jy + kz + l = z'
mx + ny + oz + p = w'3.合并公式一、二得
matlab
ax + by + cz + w = x * cos(β) - y * sin(β) // 仅当a = cos(β),b = -sin(β),c = w = 0时,等式两边成立
ex + fy + gz + h = y * cos(β) + x * sin(β) // 仅当e = sin(β) f = cos(β), g = h = 0时,等式两边成立
ix + jy + kz + l = z // 仅当 k = 1, i = j = k = 0时,等式两边成立
mx + ny + oz + p = 1 // 仅当 m = n = o = 0, p = 1时,等式两边成立4.由此得到旋转矩阵
5.通过js获取旋转矩阵
js
/**
*
* @param {number} deg 旋转的度数
* @returns 返回旋转矩阵
*/
const getRotateMatrix = (deg) => {
return new Float32Array([
Math.cos(deg),Math.sin(deg),0.0,0.0,
-Math.sin(deg),Math.cos(deg),0.0,0.0,
0.0, 0.0, 1.0,0.0,
0.0, 0.0, 0.0, 1,
]);
};6.2 实现图形旋转
js
const canvas = document.querySelector("#canvas");
const gl = canvas.getContext("webgl");
// 顶点着色器
const VERTEX_SHADER_SOURCE = `
attribute vec4 aPosition;
uniform mat4 mat;
void main() {
gl_Position = mat * aPosition;
gl_PointSize = 10.0;
}
`;
const FRAGMENT_SHADER_SOURCE = `
void main() {
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}
`;
const program = initShader(gl,VERTEX_SHADER_SOURCE,FRAGMENT_SHADER_SOURCE);
const aPosition = gl.getAttribLocation(program, "aPosition");
const mat = gl.getUniformLocation(program, "mat");
const points = new Float32Array([
-0.5, -0.5,
0.5, -0.5,
-0.5, 0.5,
]);
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);
gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(aPosition);
gl.drawArrays(gl.TRIANGLE_FAN, 0, 3);
const getRotateMatrix = (deg) => {
return new Float32Array([
Math.cos(deg),Math.sin(deg),0.0,0.0,
-Math.sin(deg),Math.cos(deg),0.0,0.0,
0.0, 0.0, 1.0,0.0,
0.0, 0.0, 0.0, 1,
]);
};
let x = 0;
const animate = () => {
x += 0.01;
const matrix = getRotateMatrix(x);
gl.uniformMatrix4fv(mat, false, matrix);
gl.drawArrays(gl.TRIANGLES, 0, 3);
requestAnimationFrame(animate);
};
animate();7.图形复合变换-矩阵组合
7.1 封装函数
使用下面方法,可以混合两个矩阵,从而达到矩阵的复合变换
js
/**
*
* @param {Float32Array} A 矩阵数组1
* @param {Float32Array} B 矩阵数组2
* @returns
*/
function mixMatrix(A, B) {
const result = new Float32Array(16);
for (let i = 0; i < 4; i++) {
result[i] = A[i] * B[0] + A[i + 4] * B[1] + A[i + 8] * B[2] + A[i + 12] * B[3]
result[i + 4] = A[i] * B[4] + A[i + 4] * B[5] + A[i + 8] * B[6] + A[i + 12] * B[7]
result[i + 8] = A[i] * B[8] + A[i + 4] * B[9] + A[i + 8] * B[10] + A[i + 12] * B[11]
result[i + 12] = A[i] * B[12] + A[i + 4] * B[13] + A[i + 8] * B[14] + A[i + 12] * B[15]
}
return result;
}7.2 实现复合变换
js
const canvas = document.querySelector("#canvas");
const gl = canvas.getContext("webgl");
// 顶点着色器
const VERTEX_SHADER_SOURCE = `
attribute vec4 aPosition;
uniform mat4 mtx;
void main() {
gl_Position = mtx * aPosition;
gl_PointSize = 10.0;
}
`;
const FRAGMENT_SHADER_SOURCE = `
void main() {
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}
`;
const program = initShader(
gl,
VERTEX_SHADER_SOURCE,
FRAGMENT_SHADER_SOURCE
);
const aPosition = gl.getAttribLocation(program, "aPosition");
const mtx = gl.getUniformLocation(program, "mtx");
// prettier-ignore
const points = new Float32Array([
-0.5, -0.5,
0.5, -0.5,
0.0, 0.5,
]);
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);
gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(aPosition);
let deg = 0;
let translateX = -1;
let scaleX = 0.1;
const animate = () => {
deg += 0.01;
translateX += 0.01;
scaleX += 0.01;
if (translateX > 1) translateX = -1;
if (scaleX > 1.5) scaleX = 0.1;
const translate = getTranslateMatrix(translateX);
const scale = getScaleMatrix(scaleX);
const rotate = getRotateMatrix(deg);
const martix = mixMatrix(mixMatrix(translate, scale), rotate);
gl.uniformMatrix4fv(mtx, false, martix);
// gl.uniformMatrix4fv(scaleMatrix, false, scale);
// gl.uniformMatrix4fv(rotateMatrix, false, rotate);
gl.drawArrays(gl.TRIANGLES, 0, 3);
requestAnimationFrame(animate);
};
animate();