引言
线性代数是计算机图形学的基础,它为图形渲染、动画、3D 建模等提供了数学工具。理解线性代数的基本概念对于掌握计算机图形学至关重要。本文将介绍计算机图形学中常用的线性代数知识,包括向量、矩阵、变换等,并通过 JavaScript 代码示例帮助理解。
向量 (Vector)
什么是向量
向量是具有大小和方向的量。在计算机图形学中,向量通常用于表示位置、方向、速度等。例如,一个二维向量可以表示平面上的一个点或方向,一个三维向量可以表示 3D 空间中的一个点或方向。
向量的基本运算
向量支持多种运算,包括加法、减法、数乘、点积和叉积等。
向量加法和减法
向量加法和减法是对应分量相加或相减。
// 向量加法
function addVectors(v1, v2) {
return [v1[0] + v2[0], v1[1] + v2[1]];
}
// 向量减法
function subtractVectors(v1, v2) {
return [v1[0] - v2[0], v1[1] - v2[1]];
}
向量数乘
向量数乘是将向量的每个分量乘以一个标量。
// 向量数乘
function multiplyVectorByScalar(v, scalar) {
return [v[0] * scalar, v[1] * scalar];
}
向量点积
向量点积是两个向量对应分量乘积的和,结果是一个标量。点积可以用来计算两个向量之间的夹角。
// 向量点积
function dotProduct(v1, v2) {
return v1[0] * v2[0] + v1[1] * v2[1];
}
向量叉积
向量叉积只在三维空间中有定义,结果是一个垂直于两个输入向量的新向量。叉积的大小等于两个向量构成的平行四边形的面积。
// 向量叉积
function crossProduct(v1, v2) {
return [ v1[1] * v2[2] - v1[2] * v2[1],
v1[2] * v2[0] - v1[0] * v2[2],
v1[0] * v2[1] - v1[1] * v2[0]
];
}
向量的模和归一化
向量的模 (长度) 是向量的大小,可以通过勾股定理计算。归一化向量是指模为 1 的向量,也称为单位向量。
// 计算向量的模
function vectorMagnitude(v) {
return Math.sqrt(v[0] * v[0] + v[1] * v[1]);
}
// 向量归一化
function normalizeVector(v) {
const magnitude = vectorMagnitude(v);
return [v[0] / magnitude, v[1] / magnitude];
}
矩阵 (Matrix)
什么是矩阵
矩阵是一个二维数组,由行和列组成。在计算机图形学中,矩阵通常用于表示变换,如旋转、缩放和平移等。
矩阵的基本运算
矩阵支持加法、减法、数乘和矩阵乘法等运算。
矩阵加法和减法
矩阵加法和减法是对应元素相加或相减,要求两个矩阵具有相同的行数和列数。
// 矩阵加法
function addMatrices(m1, m2) {
const result = [];
for (let i = 0; i < m1.length; i++) {
result[i] = [];
for (let j = 0; j < m1[i].length; j++) {
result[i][j] = m1[i][j] + m2[i][j];
}
}
return result;
}
// 矩阵减法
function subtractMatrices(m1, m2) {
const result = [];
for (let i = 0; i < m1.length; i++) {
result[i] = [];
for (let j = 0; j < m1[i].length; j++) {
result[i][j] = m1[i][j] - m2[i][j];
}
}
return result;
}
矩阵数乘
矩阵数乘是将矩阵的每个元素乘以一个标量。
// 矩阵数乘
function multiplyMatrixByScalar(m, scalar) {
const result = [];
for (let i = 0; i < m.length; i++) {
result[i] = [];
for (let j = 0; j < m[i].length; j++) {
result[i][j] = m[i][j] * scalar;
}
}
return result;
}
矩阵乘法
矩阵乘法要求第一个矩阵的列数等于第二个矩阵的行数。矩阵乘法不满足交换律。
// 矩阵乘法
function multiplyMatrices(m1, m2) {
const result = [];
for (let i = 0; i < m1.length; i++) {
result[i] = [];
for (let j = 0; j < m2[0].length; j++) {
let sum = 0;
for (let k = 0; k < m1[i].length; k++) {
sum += m1[i][k] * m2[k][j];
}
result[i][j] = sum;
}
}
return result;
}
特殊矩阵
单位矩阵
单位矩阵是一个方阵,其主对角线上的元素全为 1,其余元素全为 0。单位矩阵在矩阵乘法中相当于数字 1 的作用。
// 创建单位矩阵
function createIdentityMatrix(size) {
const matrix = [];
for (let i = 0; i < size; i++) {
matrix[i] = [];
for (let j = 0; j < size; j++) {
matrix[i][j] = i === j ? 1 : 0;
}
}
return matrix;
}
转置矩阵
转置矩阵是将原矩阵的行和列互换得到的矩阵。
// 矩阵转置
function transposeMatrix(m) {
const result = [];
for (let i = 0; i < m[0].length; i++) {
result[i] = [];
for (let j = 0; j < m.length; j++) {
result[i][j] = m[j][i];
}
}
return result;
}
变换 (Transformations)
在计算机图形学中,变换是指对图形进行平移、旋转、缩放等操作。这些变换可以用矩阵来表示。
平移 (Translation)
平移是将图形从一个位置移动到另一个位置的操作。在二维空间中,平移可以用一个 2x2 的矩阵表示。
// 创建平移矩阵
function createTranslationMatrix(tx, ty) {
return [
[1, 0, tx],
[0, 1, ty],
[0, 0, 1]
];
}
旋转 (Rotation)
旋转是将图形绕某个点旋转一定角度的操作。在二维空间中,旋转可以用一个 2x2 的矩阵表示。
// 创建旋转矩阵
function createRotationMatrix(angle) {
const cos = Math.cos(angle);
const sin = Math.sin(angle);
return [
[cos, -sin, 0],
[sin, cos, 0],
[0, 0, 1]
];
}
缩放 (Scaling)
缩放是将图形放大或缩小的操作。在二维空间中,缩放可以用一个 2x2 的矩阵表示。
// 创建缩放矩阵
function createScalingMatrix(sx, sy) {
return [
[sx, 0, 0],
[0, sy, 0],
[0, 0, 1]
];
}
组合变换
多个变换可以组合成一个变换矩阵。在组合变换时,变换的顺序很重要,因为矩阵乘法不满足交换律。
// 组合变换矩阵
function combineTransformations(matrices) {
let result = matrices[0];
for (let i = 1; i < matrices.length; i++) {
result = multiplyMatrices(result, matrices[i]);
}
return result;
}
齐次坐标 (Homogeneous Coordinates)
在计算机图形学中,为了统一处理平移、旋转和缩放等变换,引入了齐次坐标的概念。齐次坐标是在原有坐标的基础上增加一个维度。
例如,二维坐标 (x, y) 可以表示为齐次坐标 (x, y, 1),三维坐标 (x, y, z) 可以表示为齐次坐标 (x, y, z, 1)。
使用齐次坐标后,所有的变换都可以用矩阵乘法来表示,包括平移变换。
应用实例:2D 图形变换
下面是一个使用线性代数进行 2D 图形变换的完整示例。
// 向量运算
class Vector2 {
constructor(x = 0, y = 0) {
this.x = x;
this.y = y;
}
add(v) {
return new Vector2(this.x + v.x, this.y + v.y);
}
subtract(v) {
return new Vector2(this.x - v.x, this.y - v.y);
}
multiply(scalar) {
return new Vector2(this.x * scalar, this.y * scalar);
}
dot(v) {
return this.x * v.x + this.y * v.y;
}
magnitude() {
return Math.sqrt(this.x * this.x + this.y * this.y);
}
normalize() {
const mag = this.magnitude();
return new Vector2(this.x / mag, this.y / mag);
}
}
// 矩阵运算
class Matrix3 {
constructor() {
// 初始化为单位矩阵
this.data = [ [1, 0, 0],
[0, 1, 0],
[0, 0, 1]
];
}
static identity() {
return new Matrix3();
}
multiply(m) {
const result = new Matrix3();
const a = this.data;
const b = m.data;
const c = result.data;
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
c[i][j] = a[i][0] * b[0][j] + a[i][1] * b[1][j] + a[i][2] * b[2][j];
}
}
return result;
}
static translation(tx, ty) {
const m = new Matrix3();
m.data[0][2] = tx;
m.data[1][2] = ty;
return m;
}
static rotation(angle) {
const m = new Matrix3();
const c = Math.cos(angle);
const s = Math.sin(angle);
m.data[0][0] = c;
m.data[0][1] = -s;
m.data[1][0] = s;
m.data[1][1] = c;
return m;
}
static scaling(sx, sy) {
const m = new Matrix3();
m.data[0][0] = sx;
m.data[1][1] = sy;
return m;
}
transformPoint(point) {
const x = this.data[0][0] * point.x + this.data[0][1] * point.y + this.data[0][2];
const y = this.data[1][0] * point.x + this.data[1][1] * point.y + this.data[1][2];
return new Vector2(x, y);
}
}
// 应用示例
function applyTransformations() {
// 创建一个点
const point = new Vector2(10, 10);
// 创建变换矩阵
const translation = Matrix3.translation(50, 50);
const rotation = Matrix3.rotation(Math.PI / 4); // 45度
const scaling = Matrix3.scaling(2, 2);
// 组合变换
const transform = translation.multiply(rotation).multiply(scaling);
// 应用变换
const transformedPoint = transform.transformPoint(point);
console.log("原始点:", point);
console.log("变换后的点:", transformedPoint);
}
// 执行示例
applyTransformations();
总结
线性代数是计算机图形学的基础,掌握向量、矩阵和变换等基本概念对于理解和实现图形算法至关重要。本文介绍了线性代数在计算机图形学中的应用,包括向量运算、矩阵运算、变换和齐次坐标等内容,并通过 JavaScript 代码示例展示了如何实现这些概念。
在实际应用中,线性代数不仅用于基本的图形变换,还广泛应用于 3D 建模、光照计算、物理模拟等领域。深入理解线性代数将帮助你更好地掌握计算机图形学的核心技术。