计算机图形学中的数学基础:从理论到实践

261 阅读8分钟

计算机图形学是一门迷人的领域,它将数学原理与计算机编程紧密结合,创造出令人惊叹的视觉效果。无论是制作精美的游戏、逼真的动画,还是进行科学数据可视化,扎实的数学基础都是理解和实现复杂图形算法的关键。本文将深入探讨计算机图形学中不可或缺的数学知识,并通过 JavaScript 代码示例展示其实际应用。

一、向量基础

1.1 向量的定义与表示

向量是计算机图形学中的基本概念,它既有大小又有方向。在二维空间中,向量可以表示为[x, y],三维空间中则为[x, y, z]。例如,在 JavaScript 中,我们可以用数组来表示向量:

// 二维向量
let vector2D = [2, 3];
// 三维向量
let vector3D = [1, -2, 4];

1.2 向量的运算

1.2.1 向量加法

向量加法遵循平行四边形法则或三角形法则。在 JavaScript 中,实现向量加法的函数如下:

function addVectors(vectorA, vectorB) {
    return [vectorA[0] + vectorB[0], vectorA[1] + vectorB[1]];
}
// 示例
let vectorA = [1, 2];
let vectorB = [3, 4];
let result = addVectors(vectorA, vectorB);
console.log(result); // 输出: [4, 6]

1.2.2 向量减法

向量减法是加法的逆运算。其 JavaScript 实现如下:

function subtractVectors(vectorA, vectorB) {
    return [vectorA[0] - vectorB[0], vectorA[1] - vectorB[1]];
}
// 示例
let vectorC = [5, 7];
let vectorD = [2, 3];
let difference = subtractVectors(vectorC, vectorD);
console.log(difference); // 输出: [3, 4]

1.2.3 向量点积

向量点积(也称为内积)用于衡量两个向量的相似程度,其结果是一个标量。点积的计算公式为:A·B = |A|×|B|×cos(θ),其中θ是两个向量之间的夹角。在 JavaScript 中:

function dotProduct(vectorA, vectorB) {
    return vectorA[0] * vectorB[0] + vectorA[1] * vectorB[1];
}
// 示例
let vectorE = [2, 3];
let vectorF = [4, -1];
let dotResult = dotProduct(vectorE, vectorF);
console.log(dotResult); // 输出: 5

点积在图形学中有广泛应用,如计算光照模型中的光照强度,判断两个向量的方向关系等。

1.2.4 向量叉积

向量叉积仅适用于三维向量,其结果是一个与两个输入向量都垂直的向量。叉积的大小等于两个向量构成的平行四边形的面积。在 JavaScript 中:

function crossProduct(vectorA, vectorB) {
    return [
        vectorA[1] * vectorB[2] - vectorA[2] * vectorB[1],
        vectorA[2] * vectorB[0] - vectorA[0] * vectorB[2],
        vectorA[0] * vectorB[1] - vectorA[1] * vectorB[0]
    ];
}
// 示例
let vectorG = [1, 2, 3];
let vectorH = [4, 5, 6];
let crossResult = crossProduct(vectorG, vectorH);
console.log(crossResult); // 输出: [-3, 6, -3]

叉积常用于计算平面的法向量,判断物体的旋转方向等。

二、矩阵基础

2.1 矩阵的定义与表示

矩阵是一个二维数组,在计算机图形学中用于表示线性变换。例如,一个 2x2 的矩阵可以表示为:( \begin{bmatrix} a & b \ c & d \end{bmatrix} )

在 JavaScript 中,我们可以用二维数组来表示矩阵:

// 2x2矩阵
let matrix2x2 = [
    [1, 2],
    [3, 4]
];

2.2 矩阵运算

2.2.1 矩阵加法

两个矩阵相加,对应位置的元素相加即可。前提是两个矩阵的大小相同。JavaScript 实现如下:

function addMatrices(matrixA, matrixB) {
    let result = [];
    for (let i = 0; i < matrixA.length; i++) {
        let row = [];
        for (let j = 0; j < matrixA[0].length; j++) {
            row.push(matrixA[i][j] + matrixB[i][j]);
        }
        result.push(row);
    }
    return result;
}
// 示例
let matrix1 = [
    [1, 2],
    [3, 4]
];
let matrix2 = [
    [5, 6],
    [7, 8]
];
let sumMatrix = addMatrices(matrix1, matrix2);
console.log(sumMatrix); 
// 输出: 
// [
//     [6, 8],
//     [10, 12]
// ]

2.2.2 矩阵乘法

矩阵乘法较为复杂,只有当第一个矩阵的列数等于第二个矩阵的行数时,两个矩阵才能相乘。结果矩阵的元素C[i][j]等于第一个矩阵的第i行与第二个矩阵的第j列对应元素乘积之和。JavaScript 实现如下:

function multiplyMatrices(matrixA, matrixB) {
    let result = [];
    for (let i = 0; i < matrixA.length; i++) {
        let row = [];
        for (let j = 0; j < matrixB[0].length; j++) {
            let sum = 0;
            for (let k = 0; k < matrixA[0].length; k++) {
                sum += matrixA[i][k] * matrixB[k][j];
            }
            row.push(sum);
        }
        result.push(row);
    }
    return result;
}
// 示例
let matrix3 = [
    [1, 2],
    [3, 4]
];
let matrix4 = [
    [5, 6],
    [7, 8]
];
let productMatrix = multiplyMatrices(matrix3, matrix4);
console.log(productMatrix); 
// 输出: 
// [
//     [19, 22],
//     [43, 50]
// ]

在图形学中,矩阵乘法常用于复合变换,如先旋转再平移等。通过将多个变换矩阵相乘,可以得到一个综合的变换矩阵,然后应用于图形对象上。

三、三角函数

3.1 基本三角函数

在计算机图形学中,三角函数用于处理角度、旋转和几何形状等问题。JavaScript 内置了Math对象,提供了常用的三角函数方法,如Math.sin()、Math.cos()、Math.tan()等。这些函数接受的参数是弧度制,而非角度制。如果需要使用角度,需要先将其转换为弧度,转换公式为:弧度 = 角度 × (π / 180)。

例如,计算 30 度角的正弦值:

let degrees = 30;
let radians = degrees * (Math.PI / 180);
let sineValue = Math.sin(radians);
console.log(sineValue); // 输出约0.5

3.2 三角函数在图形学中的应用

在实现图形的旋转效果时,三角函数发挥着关键作用。假设我们要将一个点(x, y)绕原点旋转θ角度,旋转后的点(x', y')可以通过以下公式计算:( x' = x × cos(θ) - y × sin(θ) )

( y' = x × sin(θ) + y × cos(θ) )

在 JavaScript 中,可以实现一个旋转点的函数:

function rotatePoint(x, y, angleInRadians) {
    let cosTheta = Math.cos(angleInRadians);
    let sinTheta = Math.sin(angleInRadians);
    let newX = x * cosTheta - y * sinTheta;
    let newY = x * sinTheta + y * cosTheta;
    return [newX, newY];
}
// 示例
let point = [2, 3];
let angle = 45 * (Math.PI / 180); // 45度转换为弧度
let rotatedPoint = rotatePoint(point[0], point[1], angle);
console.log(rotatedPoint); 
// 输出: 旋转后的点坐标

此外,在计算光照模型中的入射角、反射角,以及处理曲线、曲面等几何形状时,三角函数也都有着广泛的应用。

四、线性代数在图形变换中的应用

4.1 二维图形变换

4.1.1 平移变换

平移变换是将图形在平面上移动一定的距离。对于一个点(x, y),沿x轴方向平移tx,沿y轴方向平移ty,平移后的点(x', y')为:( x' = x + tx )

( y' = y + ty )

在 JavaScript 中,可以通过以下函数实现点的平移:

function translatePoint(x, y, tx, ty) {
    return [x + tx, y + ty];
}
// 示例
let originalPoint = [1, 1];
let translatedPoint = translatePoint(originalPoint[0], originalPoint[1], 3, 2);
console.log(translatedPoint); // 输出: [4, 3]

4.1.2 缩放变换

缩放变换用于改变图形的大小。对于一个点(x, y),沿x轴方向缩放sx倍,沿y轴方向缩放sy倍,缩放后的点(x', y')为:( x' = x × sx )

( y' = y × sy )

JavaScript 实现如下:

function scalePoint(x, y, sx, sy) {
    return [x * sx, y * sy];
}
// 示例
let pointToScale = [2, 3];
let scaledPoint = scalePoint(pointToScale[0], pointToScale[1], 2, 0.5);
console.log(scaledPoint); // 输出: [4, 1.5]

4.1.3 旋转变换

前面已经介绍过通过三角函数实现点绕原点的旋转变换。在矩阵变换中,二维旋转变换可以用一个 2x2 的旋转矩阵表示:( \begin{bmatrix} cos(θ) & -sin(θ) \ sin(θ) & cos(θ) \end{bmatrix} )

将点(x, y)表示为列向量[x, y]^T,通过矩阵乘法[旋转矩阵] × [点向量]即可得到旋转后的点向量。在 JavaScript 中,可以结合前面的矩阵乘法函数实现基于矩阵的旋转变换:

function rotatePointMatrix(x, y, angleInRadians) {
    let rotationMatrix = [
        [Math.cos(angleInRadians), -Math.sin(angleInRadians)],
        [Math.sin(angleInRadians), Math.cos(angleInRadians)]
    ];
    let pointVector = [[x], [y]];
    let resultMatrix = multiplyMatrices(rotationMatrix, pointVector);
    return [resultMatrix[0][0], resultMatrix[1][0]];
}
// 示例
let pointForRotation = [3, 4];
let rotationAngle = 30 * (Math.PI / 180);
let rotatedPointMatrix = rotatePointMatrix(pointForRotation[0], pointForRotation[1], rotationAngle);
console.log(rotatedPointMatrix); 
// 输出: 基于矩阵旋转后的点坐标

4.2 三维图形变换

三维图形变换在二维变换的基础上增加了z轴方向的操作,并且涉及到更多复杂的变换矩阵。

4.2.1 三维平移变换

对于一个三维点(x, y, z),沿x轴平移tx,沿y轴平移ty,沿z轴平移tz,平移后的点(x', y', z')为:( x' = x + tx )

( y' = y + ty )

( z' = z + tz )

在 JavaScript 中实现三维点平移的函数如下:

function translatePoint3D(x, y, z, tx, ty, tz) {
    return [x + tx, y + ty, z + tz];
}
// 示例
let point3D = [1, 2, 3];
let translatedPoint3D = translatePoint3D(point3D[0], point3D[1], point3D[2], 1, -1, 2);
console.log(translatedPoint3D); // 输出: [2, 1, 5]

4.2.2 三维缩放变换

三维缩放变换同样是对x、y、z三个方向分别进行缩放。对于点(x, y, z),沿x轴缩放sx,沿y轴缩放sy,沿z轴缩放sz,缩放后的点(x', y', z')为:( x' = x × sx )

( y' = y × sy )

( z' = z × sz )

JavaScript 实现:

function scalePoint3D(x, y, z, sx, sy, sz) {
    return [x * sx, y * sy, z * sz];
}
// 示例
let pointToScale3D = [2, 3, 4];
let scaledPoint3D = scalePoint3D(pointToScale3D[0], pointToScale3D[1], pointToScale3D[2], 2, 0.5, 3);
console.log(scaledPoint3D); // 输出: [4, 1.5, 12]

4.2.3 三维旋转变换

三维旋转变换相对复杂,需要指定旋转轴和旋转角度。常见的旋转轴有x轴、y轴和z轴。以绕x轴旋转为例,旋转矩阵为:( \begin{bmatrix} 1 & 0 & 0 \ 0 & cos(θ) & -sin(θ) \ 0 & sin(θ) & cos(θ) \end{bmatrix} )

绕y轴旋转的矩阵为:( \begin{bmatrix} cos(θ) & 0 & sin(θ) \ 0 & 1 & 0 \ -sin(θ) & 0 & cos(θ) \end{bmatrix} )

绕z轴旋转的矩阵为:( \begin{bmatrix} cos(θ) & -sin(θ) & 0 \ sin(θ) & cos(θ) & 0 \ 0 & 0 & 1 \end{bmatrix} )

通过将点表示为三维列向量,与相应的旋转矩阵相乘,即可实现三维点绕特定轴的旋转。在 JavaScript 中,可以基于前面的矩阵乘法函数实现三维旋转变换。例如,实现绕x轴旋转的函数:

function rotateX(x, y, z, angleInRadians) {
    let rotationMatrix = [
        [1, 0, 0],
        [0, Math.cos(angleInRadians), -Math.sin(angleInRadians)],
        [0, Math.sin(angleInRadians), Math.cos(angleInRadians)]
    ];
    let pointVector = [[x], [y], [z]];
    let resultMatrix = multiplyMatrices(rotationMatrix, pointVector);
    return [resultMatrix[0][0], resultMatrix[1][0], resultMatrix[2][0]];
}
// 示例
let point3DForRotation = [1, 2, 3];
let rotationAngleX = 45 * (Math.PI / 180);
let rotatedPoint3DX = rotateX(point3DForRotation[0], point3DForRotation[1], point3DForRotation[2], rotationAngleX);
console.log(rotatedPoint3DX); 
// 输出: 绕x轴旋转后的三维点坐标

类似地,可以实现绕y轴和z轴旋转的函数。在实际应用中,常常需要将