glm这个变换矩阵只会出现在while循环中吗?
一、变换矩阵的三种常见使用场景
1. 动态变换(在while循环中)
每帧重新计算矩阵,实现动画效果:
while (!glfwWindowShouldClose(window))
{
// 每帧都重新计算 - 产生动态效果
float time = glfwGetTime();
float xOffset = sin(time); // 随时间变化
glm::mat4 transform = glm::mat4(1.0f);
transform = glm::translate(transform, glm::vec3(xOffset, 0.0f, 0.0f));
// 传递给着色器并绘制...
}
2. 静态变换(在while循环外)
只在程序开始时计算一次,之后保持不变:
int main()
{
// 初始化...
// 在循环外计算一次,整个程序运行期间都不变
glm::mat4 transform = glm::mat4(1.0f);
transform = glm::translate(transform, glm::vec3(0.5f, 0.5f, 0.0f));
transform = glm::scale(transform, glm::vec3(0.5f, 0.5f, 1.0f));
// 获取uniform位置(也可以只做一次)
unsigned int transformLoc = glGetUniformLocation(ourShader.ID, "transform");
while (!glfwWindowShouldClose(window))
{
// 清屏、绑定纹理等...
ourShader.use();
// 使用预先计算好的矩阵(每帧都一样)
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(transform));
glDrawElements(...);
}
}
适用场景:场景中静止的物体(建筑、地形等)。
3. 混合使用(部分静态 + 部分动态)
int main()
{
// 静态部分:基础变换(只计算一次)
glm::mat4 baseTransform = glm::mat4(1.0f);
baseTransform = glm::scale(baseTransform, glm::vec3(0.5f, 0.5f, 1.0f));
unsigned int transformLoc = glGetUniformLocation(ourShader.ID, "transform");
while (!glfwWindowShouldClose(window))
{
// 动态部分:每帧添加旋转
glm::mat4 transform = baseTransform; // 从静态矩阵开始
float time = glfwGetTime();
transform = glm::rotate(transform, time, glm::vec3(0.0f, 0.0f, 1.0f));
ourShader.use();
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(transform));
glDrawElements(...);
}
}
适用场景:物体本身有固定缩放和位置,但还要自转。
二、性能考虑
每帧都计算矩阵的性能开销
// 轻量级操作(没问题)
transform = glm::translate(transform, glm::vec3(xOffset, 0.0f, 0.0f));
// 重量级操作(尽量避免每帧都做)
transform = glm::inverse(someComplexMatrix); // 矩阵求逆很慢
transform = glm::perspective(...); // 投影矩阵通常只计算一次
最佳实践:
-
视图矩阵、投影矩阵:通常在循环外计算一次,除非摄像机在移动
-
模型矩阵:如果物体在移动/旋转,就在循环内计算
三、实际例子对比
错误示例:不必要的重复计算
while (!glfwWindowShouldClose(window))
{
// 投影矩阵不会变,却每帧都计算(浪费性能)
glm::mat4 projection = glm::perspective(glm::radians(45.0f), 800.0f/600.0f, 0.1f, 100.0f);
// 视图矩阵也不会变(摄像机不动)
glm::mat4 view = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, -3.0f));
// 只有模型矩阵在变化
glm::mat4 model = glm::rotate(glm::mat4(1.0f), time, glm::vec3(0.0f, 1.0f, 0.0f));
// 传递三个矩阵...
}
正确示例:优化性能
int main()
{
// 在循环外计算一次
glm::mat4 projection = glm::perspective(glm::radians(45.0f), 800.0f/600.0f, 0.1f, 100.0f);
glm::mat4 view = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, -3.0f));
// 获取uniform位置(只做一次)
unsigned int projectionLoc = glGetUniformLocation(shader.ID, "projection");
unsigned int viewLoc = glGetUniformLocation(shader.ID, "view");
unsigned int modelLoc = glGetUniformLocation(shader.ID, "model");
while (!glfwWindowShouldClose(window))
{
float time = glfwGetTime();
// 只有模型矩阵需要每帧更新
glm::mat4 model = glm::rotate(glm::mat4(1.0f), time, glm::vec3(0.0f, 1.0f, 0.0f));
shader.use();
glUniformMatrix4fv(projectionLoc, 1, GL_FALSE, glm::value_ptr(projection));
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
glDrawElements(...);
}
}
四、特殊情况
响应窗口大小变化
投影矩阵通常需要在窗口大小改变时重新计算:
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
// 重新计算投影矩阵(适应新的宽高比)
glm::mat4 projection = glm::perspective(glm::radians(45.0f), (float)width/height, 0.1f, 100.0f);
// 然后需要重新传递给着色器...
}
用户输入控制
根据键盘输入动态更新矩阵:
glm::mat4 view = glm::mat4(1.0f);
float cameraX = 0.0f, cameraY = 0.0f;
while (!glfwWindowShouldClose(window))
{
// 根据按键更新位置
if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
cameraX -= 0.01f;
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
cameraX += 0.01f;
// 每帧重新计算视图矩阵(因为摄像机在移动)
view = glm::translate(glm::mat4(1.0f), glm::vec3(cameraX, cameraY, -3.0f));
// 传递给着色器...
}
总结
| 情况 | 计算位置 | 原因 |
|---|---|---|
| 静态物体 | while循环外 | 变换不变,避免重复计算 |
| 动画物体 | while循环内 | 每帧都需要新位置 |
| 投影矩阵 | while循环外 | 通常不变,除非窗口大小改变 |
| 跟随鼠标/键盘 | while循环内 | 需要响应用户输入 |
记住原则:
-
变的东西 → 放循环内
-
不变的东西 → 放循环外
更多推荐



所有评论(0)