变换与矩阵
在3D图形学中,变换是将一个几何对象从一个位置、方向和大小转换到另一个位置、方向和大小的过程。矩阵是实现这些变换的强大工具。通过矩阵乘法,可以高效地实现平移、旋转和缩放等变换。
一、矩阵变换
(一)平移变换
平移变换是将一个对象沿着某个方向移动一定的距离。在3D空间中,平移可以通过一个4x4的平移矩阵实现。平移矩阵的形式如下:
其中,tx
、ty
和tz
分别表示在x、y和z方向上的平移量。
(二)旋转变换
旋转变换是将一个对象围绕某个轴旋转一定的角度。在3D空间中,旋转变换可以通过一个4x4的旋转矩阵实现。例如,围绕z轴旋转θ
度的旋转矩阵如下:
类似地,可以定义围绕x轴和y轴的旋转矩阵。
(三)缩放变换
缩放变换是将一个对象在某个方向上放大或缩小一定的比例。在3D空间中,缩放变换可以通过一个4x4的缩放矩阵实现。缩放矩阵的形式如下:
其中,sx
、sy
和sz
分别表示在x、y和z方向上的缩放比例。
二、模型视图矩阵
模型视图矩阵(Model-View Matrix)是将一个对象从模型空间转换到视图空间的矩阵。模型空间是对象的局部坐标系,而视图空间是相对于观察者的坐标系。模型视图矩阵通常由以下两部分组成:
- 模型矩阵(Model Matrix):将对象从模型空间转换到世界空间。
- 视图矩阵(View Matrix):将对象从世界空间转换到视图空间。
模型视图矩阵可以通过模型矩阵和视图矩阵的乘积得到:
(一)模型矩阵
模型矩阵用于描述对象在世界空间中的位置、方向和大小。它可以通过平移、旋转和缩放矩阵的组合来构建。例如,先平移,再旋转,最后缩放:
(二)视图矩阵
视图矩阵用于描述观察者的位置和方向。它通常通过一个“观察矩阵”来构建,该矩阵将世界空间中的对象转换到观察者的坐标系中。视图矩阵可以通过以下参数构建:
- 观察者的位置(eye):观察者的坐标。
- 目标点(at):观察者正在看的点。
- 上方向(up):定义观察者的“上”方向。
视图矩阵可以通过以下公式计算:
三、投影矩阵
投影矩阵(Projection Matrix)用于将3D空间中的对象投影到2D屏幕上。有两种主要的投影方式:正交投影和透视投影。
(一)正交投影
正交投影是一种平行投影,其中投影线与投影平面垂直。正交投影不会产生透视效果,适用于绘制等轴测图和2D图形。正交投影矩阵可以通过以下参数构建:
- 左(left):视口的左边界。
- 右(right):视口的右边界。
- 底(bottom):视口的底边界。
- 顶(top):视口的顶边界。
- 近(near):视口的近平面。
- 远(far):视口的远平面。
正交投影矩阵的形式如下:
(二)透视投影
透视投影是一种模拟人眼视觉的投影方式,其中远离观察者的对象看起来更小。透视投影矩阵可以通过以下参数构建:
- 视场角(fovy):垂直方向的视场角(以度为单位)。
- 宽高比(aspect):视口的宽高比。
- 近(near):视口的近平面。
- 远(far):视口的远平面。
透视投影矩阵的形式如下:
四、完整的WebGL变换示例
以下是一个完整的WebGL示例,展示如何使用矩阵实现平移、旋转和缩放变换,并将对象从模型空间转换到视图空间和投影空间。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>WebGL Transformation Example</title>
</head>
<body>
<canvas id="webglCanvas" width="800" height="600"></canvas>
<script>
const canvas = document.getElementById('webglCanvas');
const gl = canvas.getContext('webgl');
if (!gl) {
alert('Your browser does not support WebGL');
}
// 顶点着色器代码
const vertexShaderSource = `
attribute vec4 a_position;
uniform mat4 u_modelViewProjectionMatrix;
void main() {
gl_Position = u_modelViewProjectionMatrix * a_position;
}
`;
// 片元着色器代码
const fragmentShaderSource = `
precision mediump float;
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); // 红色
}
`;
// 创建顶点着色器
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexShaderSource);
gl.compileShader(vertexShader);
// 创建片元着色器
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentShaderSource);
gl.compileShader(fragmentShader);
// 创建程序
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
alert('Unable to initialize the shader program: ' + gl.getProgramInfoLog(program));
}
// 使用程序
gl.useProgram(program);
// 定义顶点数据
const vertices = [
-0.5, -0.5, 0.0,
0.5, -0.5, 0.0,
0.0, 0.5, 0.0
];
// 创建顶点缓冲区对象
const vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
// 获取attribute变量的位置
const positionLocation = gl.getAttribLocation(program, 'a_position');
// 绑定缓冲区到attribute变量
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0);
// 获取uniform变量的位置
const modelViewProjectionMatrixLocation = gl.getUniformLocation(program, 'u_modelViewProjectionMatrix');
// 创建矩阵
const modelMatrix = mat4.create();
const viewMatrix = mat4.create();
const projectionMatrix = mat4.create();
const modelViewProjectionMatrix = mat4.create();
// 设置模型矩阵(平移、旋转、缩放)
mat4.translate(modelMatrix, modelMatrix, [0.0, 0.0, -2.0]); // 平移
mat4.rotate(modelMatrix, modelMatrix, Math.PI / 4, [0, 0, 1]); // 旋转45度
mat4.scale(modelMatrix, modelMatrix, [1.0, 1.0, 1.0]); // 缩放
// 设置视图矩阵(观察者位置和方向)
mat4.lookAt(viewMatrix, [0, 0, 5], [0, 0, 0], [0, 1, 0]);
// 设置投影矩阵(透视投影)
mat4.perspective(projectionMatrix, Math.PI / 4, canvas.width / canvas.height, 0.1, 100.0);
// 计算模型视图投影矩阵
mat4.multiply(modelViewProjectionMatrix, viewMatrix, modelMatrix);
mat4.multiply(modelViewProjectionMatrix, projectionMatrix, modelViewProjectionMatrix);
// 将矩阵传递给着色器
gl.uniformMatrix4fv(modelViewProjectionMatrixLocation, false, modelViewProjectionMatrix);
// 清空画布
gl.clearColor(0.0, 0.0, 0.0, 1.0); // 设置背景颜色为黑色
gl.clear(gl.COLOR_BUFFER_BIT);
// 绘制三角形
gl.drawArrays(gl.TRIANGLES, 0, 3);
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/3.4.3/gl-matrix-min.js"></script>
</body>
</html>
代码解析
- 顶点着色器:通过
u_modelViewProjectionMatrix
将顶点从模型空间转换到裁剪空间。 - 片元着色器:设置片元颜色为红色。
- 矩阵操作:
- 使用
gl-matrix
库创建和操作矩阵。 - 设置模型矩阵(平移、旋转、缩放)。
- 设置视图矩阵(观察者位置和方向)。
- 设置投影矩阵(透视投影)。
- 计算模型视图投影矩阵,并传递给着色器。
- 使用
- 绘制命令:使用
gl.drawArrays
绘制三角形。
通过这些矩阵操作,可以实现对象的平移、旋转和缩放,并将其从模型空间转换到视图空间和投影空间。