计算机图形学中的线性代数基础

263 阅读7分钟

引言

线性代数是计算机图形学的基础,它为图形渲染、动画、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 建模、光照计算、物理模拟等领域。深入理解线性代数将帮助你更好地掌握计算机图形学的核心技术。