相机与变换:变换矩阵
变换矩阵(Transformation Matrices)
在本节中,我们将深入探讨 3D 图形学中使用的变换矩阵,以及它们在渲染管线中的应用方式。
模型 – 视图 – 投影(MVP)管线
顶点从物体空间转换到屏幕空间的过程涉及一系列矩阵乘法,这一过程通常被称为 MVP 管线:
// 完整的变换管线
glm::mat4 MVP = projectionMatrix * viewMatrix * modelMatrix;
下面我们详细解析每个矩阵的作用。
模型矩阵(Model Matrix)
模型矩阵将顶点从物体空间变换到世界空间,用于在世界坐标系中定位、旋转和缩放物体。
glm::mat4 createModelMatrix(
const glm::vec3& position,
const glm::vec3& rotation,
const glm::vec3& scale
) {
glm::mat4 model = glm::mat4(1.0f);
// 按顺序应用变换:缩放 → 旋转 → 平移
model = glm::translate(model, position);
// 绕各轴应用旋转变换
model = glm::rotate(model, glm::radians(rotation.x), glm::vec3(1.0f, 0.0f, 0.0f));
model = glm::rotate(model, glm::radians(rotation.y), glm::vec3(0.0f, 1.0f, 0.0f));
model = glm::rotate(model, glm::radians(rotation.z), glm::vec3(0.0f, 0.0f, 1.0f));
// 应用缩放变换
model = glm::scale(model, scale);
return model;
}
视图矩阵(View Matrix)
视图矩阵将顶点从世界空间变换到视图空间(相机空间),它代表了相机的位置和朝向。
glm::mat4 createViewMatrix(
const glm::vec3& cameraPosition,
const glm::vec3& cameraTarget,
const glm::vec3& upVector
) {
return glm::lookAt(cameraPosition, cameraTarget, upVector);
}
lookAt函数会创建一个视图矩阵,将相机放置在cameraPosition位置,朝向cameraTarget目标点,同时通过upVector定义相机的上方向。
投影矩阵(Projection Matrix)
投影矩阵将顶点从视图空间变换到裁剪空间,它定义了 3D 坐标如何投影到 2D 屏幕上。
透视投影(Perspective Projection)
透视投影模拟了人眼的视觉效果:物体距离越远,显示尺寸越小。
glm::mat4 createPerspectiveMatrix(
float fovY,
float aspectRatio,
float nearPlane,
float farPlane
) {
return glm::perspective(glm::radians(fovY), aspectRatio, nearPlane, farPlane);
}
参数说明:
- fovY:垂直方向的视野角度(单位:度)
- aspectRatio:视口的宽高比(宽度 / 高度)
- nearPlane:近裁剪面的距离
- farPlane:远裁剪面的距离
正交投影(Orthographic Projection)
正交投影不存在透视畸变,适用于 2D 渲染或技术绘图场景。
glm::mat4 createOrthographicMatrix(
float left,
float right,
float bottom,
float top,
float nearPlane,
float farPlane
) {
return glm::ortho(left, right, bottom, top, nearPlane, farPlane);
}
法向量矩阵(Normal Matrix)
当对物体应用非均匀缩放时,如果直接使用模型矩阵变换法向量,会导致法向量方向出错。法向量矩阵正是为解决这一问题而生:
glm::mat3 createNormalMatrix(const glm::mat4& modelMatrix) {
// 法向量矩阵是模型矩阵左上角3×3子矩阵的逆矩阵的转置
return glm::transpose(glm::inverse(glm::mat3(modelMatrix)));
}
在着色器中应用变换
在 Vulkan 中,我们通常将这些矩阵作为统一变量(uniform)传递给着色器:
// 顶点着色器
#version 450
layout(binding = 0) uniform UniformBufferObject {
mat4 model;
mat4 view;
mat4 proj;
} ubo;
layout(location = 0) in vec3 inPosition;
layout(location = 1) in vec3 inNormal;
layout(location = 2) in vec2 inTexCoord;
layout(location = 0) out vec3 fragNormal;
layout(location = 1) out vec2 fragTexCoord;
void main() {
// 应用MVP变换
gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 1.0);
// 使用法向量矩阵变换法向量
mat3 normalMatrix = transpose(inverse(mat3(ubo.model)));
fragNormal = normalMatrix * inNormal;
fragTexCoord = inTexCoord;
}
层级变换(Hierarchical Transformations)
对于存在父子关系的复杂物体或场景,我们需要使用层级变换:
// 父物体变换
glm::mat4 parentModel = createModelMatrix(parentPosition, parentRotation, parentScale);
// 子物体相对于父物体的局部变换
glm::mat4 localModel = createModelMatrix(childLocalPosition, childLocalRotation, childLocalScale);
// 组合变换(子物体的世界空间变换矩阵)
glm::mat4 childWorldModel = parentModel * localModel;
在下一节中,我们将基于这些变换概念,实现一个能够在 3D 场景中自由导航的相机系统。



