WebGL 矩阵变换详解:平移旋转缩放与复合动画
介绍 WebGL 中矩阵变换的核心原理与实战应用。涵盖平移、旋转、缩放三种基础变换的 4x4 矩阵构建方法,以及使用 gl-matrix 库实现复合变换的技巧。通过代码示例演示 CPU 端构建矩阵并传入 GPU 着色器的流程,强调变换顺序对最终效果的影响,帮助开发者理解 WebGL 渲染管线中的坐标转换机制。

介绍 WebGL 中矩阵变换的核心原理与实战应用。涵盖平移、旋转、缩放三种基础变换的 4x4 矩阵构建方法,以及使用 gl-matrix 库实现复合变换的技巧。通过代码示例演示 CPU 端构建矩阵并传入 GPU 着色器的流程,强调变换顺序对最终效果的影响,帮助开发者理解 WebGL 渲染管线中的坐标转换机制。

WebGL(Web Graphics Library)是浏览器原生的 3D/2D 渲染 API,无需插件、直接调用 GPU 加速 —— 但想要玩转 WebGL 动画,矩阵乘法是绕不开的核心!
WebGL 基于 OpenGL ES,核心是着色器;而矩阵是连接「JavaScript 逻辑」和「GPU 渲染」的桥梁:
uniform 变量关键:WebGL 中所有变换的标准写法是
gl_Position = 变换矩阵 * 顶点坐标,而非直接修改顶点 x/y 分量!
平移是最基础的仿射变换,用 4x4 矩阵表示后,能和其他变换无缝组合!
在计算机图形学中,平移的数学原理通过齐次坐标 + 4x4 矩阵实现:
text
[ 1 0 0 tx ] [ 0 1 0 ty ] [ 0 0 1 tz ] [ 0 0 0 1 ]
矩阵乘法计算过程:
对三维点 (x,y,z,1) 执行矩阵乘法:
text
x' = 1*x + 0*y + 0*z + tx*1 = x + tx
y' = 0*x + 1*y + 0*z + ty*1 = y + ty
z' = 0*x + 0*y + 1*z + tz*1 = z + tz
w' = 0*x + 0*y + 0*z + 1*1 = 1
注:WebGL 中即使处理 2D 图形,也统一使用 4x4 矩阵(z/tz 设为 0),这是 GPU 原生支持的标准格式。
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>WebGL 矩阵实战:平移的红色三角形</title>
<style>
canvas { border: 2px solid #ff4400; display: block; margin: 20px auto; border-radius: 8px; }
</style>
</head>
<body>
<canvas></canvas>
<script>
// 1. 获取 Canvas 和 WebGL 上下文
const canvas = document.getElementById('glCanvas');
const gl = canvas.getContext('webgl');
if (!gl) {
alert('您的浏览器不支持 WebGL!换 Chrome/Firefox 试试');
throw new Error('WebGL not supported');
}
// 2. 设置黑色背景
gl.clearColor(0.0, 0.0, , );
vertexShaderSource = ;
fragmentShaderSource = ;
() {
shader = gl.(type);
gl.(shader, source);
gl.(shader);
(!gl.(shader, gl.)) {
.(, gl.(shader));
gl.(shader);
;
}
shader;
}
vertexShader = (gl, vertexShaderSource, gl.);
fragmentShader = (gl, fragmentShaderSource, gl.);
program = gl.();
gl.(program, vertexShader);
gl.(program, fragmentShader);
gl.(program);
gl.(program);
vertices = ([
-, -, ,
, -, ,
, ,
]);
vertexBuffer = gl.();
gl.(gl., vertexBuffer);
gl.(gl., vertices, gl.);
a_Position = gl.(program, );
gl.(a_Position, , gl., , , );
gl.(a_Position);
u_TranslateMatrix = gl.(program, );
translateMatrix = ();
tx = ;
step = ;
() {
matrix.([
, , , ,
, , , ,
, , , ,
tx, ty, tz,
]);
}
() {
tx += step;
(tx > ) tx = -;
(translateMatrix, tx, , );
gl.(u_TranslateMatrix, , translateMatrix);
gl.(gl.);
gl.(gl., , );
(animate);
}
();
红色三角形沿 x 轴平移,本质是不断更新 4x4 平移矩阵的 tx 分量,GPU 通过「矩阵 × 顶点」计算新位置!

旋转的核心是「4x4 旋转矩阵」,纯矩阵乘法写法更易和其他变换组合!
二维点绕原点逆时针旋转 θ 角的 4x4 矩阵(WebGL 标准格式):
text
[ cosθ -sinθ 0 0 ]
[ sinθ cosθ 0 0 ]
[ 0 0 1 0 ]
[ 0 0 0 1 ]
矩阵乘法计算过程:
对二维点 (x,y)(齐次坐标 (x,y,0,1))执行矩阵乘法:
text
x' = cosθ * x + (-sinθ) * y + 0 * 0 + 0 * 1 = x*cosθ - y*sinθ
y' = sinθ * x + cosθ * y + 0 * 0 + 0 * 1 = x*sinθ + y*cosθ
z' = 0 * x + 0 * y + 1 * 0 + 0 * 1 = 0
w' = 0 * x + 0 * y + 0 * 0 + 1 * 1 = 1
矩阵乘法结果等价于:
text
x' = x*cosθ - y*sinθ
y' = x*sinθ + y*cosθ
关键:矩阵形式无需在着色器中写三角函数,只需传递完整矩阵,更符合 GPU 渲染规范!
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>WebGL 矩阵实战:旋转的红色三角形</title>
<style>
canvas { border: 2px solid #ff4400; display: block; margin: 20px auto; border-radius: 8px; }
</style>
</head>
<body>
<canvas></canvas>
<script>
// 1. 获取上下文
const canvas = document.getElementById('glCanvas');
const gl = canvas.getContext('webgl');
if (!gl) {
alert('换 Chrome/Firefox 试试!');
throw new Error('WebGL not supported');
}
gl.clearColor(0.0, 0.0, 0.0, 1.0);
vertexShaderSource = ;
fragmentShaderSource = ;
() {
shader = gl.(type);
gl.(shader, source);
gl.(shader);
(!gl.(shader, gl.)) {
.(, gl.(shader));
gl.(shader);
;
}
shader;
}
vertexShader = (gl, vertexShaderSource, gl.);
fragmentShader = (gl, fragmentShaderSource, gl.);
program = gl.();
gl.(program, vertexShader);
gl.(program, fragmentShader);
gl.(program);
gl.(program);
vertices = ([
, , ,
-, -, ,
, -,
]);
vertexBuffer = gl.();
gl.(gl., vertexBuffer);
gl.(gl., vertices, gl.);
a_Position = gl.(program, );
gl.(a_Position, , gl., , , );
gl.(a_Position);
u_RotateMatrix = gl.(program, );
rotateMatrix = ();
angle = ;
() {
cos = .(angle);
sin = .(angle);
matrix.([
cos, -sin, , ,
sin, cos, , ,
, , , ,
, , ,
]);
}
() {
angle += ;
(rotateMatrix, angle);
gl.(u_RotateMatrix, , rotateMatrix);
gl.(gl.);
gl.(gl., , );
(animateRotate);
}
();
三角形绕原点旋转,本质是不断更新 4x4 旋转矩阵的 cosθ/sinθ 分量,GPU 自动完成「矩阵 × 顶点」运算!

缩放是改变图形大小的核心变换,同样通过 4x4 矩阵乘法实现,是复合变换的重要组成部分!
二维点沿 x/y 轴缩放的 4x4 矩阵(WebGL 标准格式):
text
[ sx 0 0 0 ]
[ 0 sy 0 0 ]
[ 0 0 sz 0 ]
[ 0 0 0 1 ]
sx:x 轴缩放因子(>1 放大,<1 缩小,负数翻转)sy:y 轴缩放因子sz:z 轴缩放因子(2D 场景设为 1)对二维点 (x,y)(齐次坐标 (x,y,0,1))执行矩阵乘法:
text
x' = sx * x + 0 * y + 0 * 0 + 0 * 1 = x * sx
y' = 0 * x + sy * y + 0 * 0 + 0 * 1 = y * sy
z' = 0 * x + 0 * y + sz * 0 + 0 * 1 = 0 (2D 场景 sz=1)
w' = 0 * x + 0 * y + 0 * 0 + 1 * 1 = 1
矩阵乘法结果等价于:
text
x' = x * sx
y' = y * sy
关键:缩放矩阵的核心是对角线上的缩放因子,非对角线元素均为 0,保证仅改变大小不改变位置(绕原点缩放)。
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>WebGL 矩阵实战:缩放的红色三角形</title>
<style>
canvas { border: 2px solid #ff4400; display: block; margin: 20px auto; border-radius: 8px; }
</style>
</head>
<body>
<canvas></canvas>
<script>
// 1. 获取上下文
const canvas = document.getElementById('glCanvas');
const gl = canvas.getContext('webgl');
if (!gl) {
alert('您的浏览器不支持 WebGL!换 Chrome/Firefox 试试');
throw new Error('WebGL not supported');
}
gl.clearColor(0.0, 0.0, 0.0, );
vertexShaderSource = ;
fragmentShaderSource = ;
() {
shader = gl.(type);
gl.(shader, source);
gl.(shader);
(!gl.(shader, gl.)) {
.(, gl.(shader));
gl.(shader);
;
}
shader;
}
vertexShader = (gl, vertexShaderSource, gl.);
fragmentShader = (gl, fragmentShaderSource, gl.);
program = gl.();
gl.(program, vertexShader);
gl.(program, fragmentShader);
gl.(program);
gl.(program);
vertices = ([
, , ,
-, -, ,
, -,
]);
vertexBuffer = gl.();
gl.(gl., vertexBuffer);
gl.(gl., vertices, gl.);
a_Position = gl.(program, );
gl.(a_Position, , gl., , , );
gl.(a_Position);
u_ScaleMatrix = gl.(program, );
scaleMatrix = ();
scaleFactor = ;
step = ;
isEnlarging = ;
() {
matrix.([
sx, , , ,
, sy, , ,
, , sz, ,
, , ,
]);
}
() {
(isEnlarging) {
scaleFactor += step;
(scaleFactor >= ) isEnlarging = ;
} {
scaleFactor -= step;
(scaleFactor <= ) isEnlarging = ;
}
(scaleMatrix, scaleFactor, scaleFactor, );
gl.(u_ScaleMatrix, , scaleMatrix);
gl.(gl.);
gl.(gl., , );
(animateScale);
}
();
三角形绕原点在 0.5 倍~2 倍之间来回缩放,本质是不断更新 4x4 缩放矩阵的 sx/sy 分量,GPU 自动完成「矩阵 × 顶点」运算!

手动组合多个矩阵容易出错?gl-matrix 库可一键完成「平移 + 旋转 + 缩放」的矩阵乘法,是工业级开发的首选方案!
mat4.translate()/mat4.rotateZ()/mat4.scale() 直接生成变换矩阵<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>WebGL 矩阵进阶:平移 + 旋转 + 缩放复合动画</title>
<style>
canvas { border: 2px solid #ff4400; display: block; margin: 20px auto; border-radius: 8px; }
</style>
<!-- 引入 gl-matrix 库(直接用 CDN) -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.8.1/gl-matrix-min.js"></script>
</head>
<body>
<canvas></canvas>
<script>
// 1. 获取上下文
const canvas = document.getElementById('glCanvas');
const gl = canvas.getContext('webgl');
if (!gl) {
alert('您的浏览器不支持 WebGL!换 Chrome/Firefox 试试');
();
}
gl.(, , , );
vertexShaderSource = ;
fragmentShaderSource = ;
() {
shader = gl.(type);
gl.(shader, source);
gl.(shader);
(!gl.(shader, gl.)) {
.(, gl.(shader));
gl.(shader);
;
}
shader;
}
vertexShader = (gl, vertexShaderSource, gl.);
fragmentShader = (gl, fragmentShaderSource, gl.);
program = gl.();
gl.(program, vertexShader);
gl.(program, fragmentShader);
gl.(program);
gl.(program);
vertices = ([
, , ,
-, -, ,
, -,
]);
vertexBuffer = gl.();
gl.(gl., vertexBuffer);
gl.(gl., vertices, gl.);
a_Position = gl.(program, );
gl.(a_Position, , gl., , , );
gl.(a_Position);
u_ModelMatrix = gl.(program, );
modelMatrix = mat4.();
tx = ;
angle = ;
scaleFactor = ;
isEnlarging = ;
() {
mat4.(modelMatrix);
mat4.(modelMatrix, modelMatrix, [scaleFactor, scaleFactor, ]);
mat4.(modelMatrix, modelMatrix, angle);
mat4.(modelMatrix, modelMatrix, [tx, , ]);
tx += ;
(tx > ) tx = -;
angle += ;
(isEnlarging) {
scaleFactor += ;
(scaleFactor >= ) isEnlarging = ;
} {
scaleFactor -= ;
(scaleFactor <= ) isEnlarging = ;
}
gl.(u_ModelMatrix, , modelMatrix);
gl.(gl.);
gl.(gl., , );
(animateWithMatrix);
}
();
三角形同时完成「沿 x 轴平移 + 绕原点旋转 + 0.7~1.5 倍缩放」的复合动画,gl-matrix 自动完成所有矩阵乘法运算,代码简洁且高效!

mat4.identity() 生成单位矩阵(无变换),是所有矩阵变换的起点Float32Array 类型的 16 个元素(列主序),传递时 gl.uniformMatrix4fv 的第二个参数必须为 falsetx/angle/scaleFactor 数值,看动画变化,直观理解矩阵作用
微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online