OpenGL笔记十六之视图变换矩阵实验-lookAt函数
—— 2024-07-30 晚上
bilibili赵新政老师的教程看后笔记
code review!
文章目录
1.glm::lookAt 函数
glm::lookAt
函数是 OpenGL 数学库 GLM (OpenGL Mathematics) 中用于生成视图矩阵的函数。视图矩阵通常用于将世界坐标系转换为视图坐标系,从而模拟摄像机的视角。glm::lookAt
函数定义如下glm::lookAt
函数是 OpenGL 数学库 GLM (OpenGL Mathematics) 中用于生成视图矩阵的函数。视图矩阵通常用于将世界坐标系转换为视图坐标系,从而模拟摄像机的视角。glm::lookAt
函数定义如下:
glm::mat4 glm::lookAt(
glm::vec3 const &eye,
glm::vec3 const ¢er,
glm::vec3 const &up
);
参数详解
- eye: 摄像机的位置,即视点 (View Position)。
- center: 摄像机观察的目标位置,即目标点 (Target Position)。
- up: 定义摄像机的上方向 (Up Vector)。
返回值
glm::lookAt
返回一个 glm::mat4
类型的 4x4 视图矩阵。
工作原理
glm::lookAt
函数通过以下几个步骤生成视图矩阵:
-
定义摄像机的坐标系:
- Z 轴 (Forward Vector): 计算方向向量
f
,即从eye
指向center
的向量,并归一化。 - X 轴 (Right Vector): 计算右方向向量
s
,即f
与up
的叉积,并归一化。 - Y 轴 (True Up Vector): 计算新的上方向向量
u
,即s
与f
的叉积。
- Z 轴 (Forward Vector): 计算方向向量
-
构建旋转矩阵:
通过s
、u
和-f
生成旋转矩阵。 -
构建平移矩阵:
通过eye
的负值生成平移矩阵。 -
组合变换矩阵:
将旋转矩阵和平移矩阵相乘,得到最终的视图矩阵。
视图矩阵计算的过程
glm::lookAt
函数生成的视图矩阵的输出依赖于传递的参数。假设摄像机的位置 (eye
) 为 (0.0f, 0.0f, 5.0f)
,目标位置 (center
) 为 (0.0f, 0.0f, 0.0f)
,上方向 (up
) 为 (0.0f, 1.0f, 0.0f)
。可以手动计算出视图矩阵,或者直接通过代码输出。这是手动计算的过程:
-
计算方向向量 (Forward Vector)
f
:glm::vec3 f = glm::normalize(center - eye); // f = normalize((0, 0, 0) - (0, 0, 5)) = normalize((0, 0, -5)) = (0, 0, -1)
-
计算右方向向量 (Right Vector)
s
:glm::vec3 s = glm::normalize(glm::cross(f, up)); // s = normalize(cross((0, 0, -1), (0, 1, 0))) = normalize((1, 0, 0)) = (1, 0, 0)
-
计算新的上方向向量 (True Up Vector)
u
:glm::vec3 u = glm::cross(s, f); // u = cross((1, 0, 0), (0, 0, -1)) = (0, 1, 0)
-
构建旋转矩阵:
glm::mat4 rotation = glm::mat4( glm::vec4(s, 0.0f), glm::vec4(u, 0.0f), glm::vec4(-f, 0.0f), glm::vec4(0.0f, 0.0f, 0.0f, 1.0f) ); // rotation = // [ 1, 0, 0, 0 ] // [ 0, 1, 0, 0 ] // [ 0, 0, 1, 0 ] // [ 0, 0, 0, 1 ]
-
构建平移矩阵:
glm::mat4 translation = glm::translate(glm::mat4(1.0f), -eye); // translation = // [ 1, 0, 0, 0 ] // [ 0, 1, 0, 0 ] // [ 0, 0, 1, -5 ] // [ 0, 0, 0, 1 ]
-
组合变换矩阵:
glm::mat4 viewMatrix = rotation * translation; // viewMatrix = // [ 1, 0, 0, 0 ] // [ 0, 1, 0, 0 ] // [ 0, 0, 1, -5 ] // [ 0, 0, 0, 1 ]
输出结果
运行示例代码将生成并打印视图矩阵:
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <iostream>
int main() {
glm::vec3 eye(0.0f, 0.0f, 5.0f);
glm::vec3 center(0.0f, 0.0f, 0.0f);
glm::vec3 up(0.0f, 1.0f, 0.0f);
glm::mat4 viewMatrix = glm::lookAt(eye, center, up);
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
std::cout << viewMatrix[i][j] << " ";
}
std::cout << std::endl;
}
return 0;
}
输出如下:
1 0 0 0
0 1 0 0
0 0 1 -5
0 0 0 1
这个结果与手动计算的视图矩阵一致。该矩阵将摄像机从 (0, 0, 5)
平移到原点,并保持其方向不变。
2.课程代码运行
3.vs
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout (location = 2) in vec2 aUV;
out vec3 color;
out vec2 uv;
uniform mat4 transform;
uniform mat4 viewMatrix;
//aPos作为attribute(属性)传入shader
//不允许更改的
void main()
{
vec4 position = vec4(aPos, 1.0);
position = viewMatrix * transform * position;
gl_Position = position;
color = aColor;
uv = aUV;
}
4.fs
#version 330 core
out vec4 FragColor;
in vec3 color;
in vec2 uv;
uniform sampler2D sampler;
void main()
{
FragColor = texture(sampler, uv);
}
5.main.cpp
#include <iostream>
#include "glframework/core.h"
#include "glframework/shader.h"
#include <string>
#include <assert.h>//断言
#include "wrapper/checkError.h"
#include "application/Application.h"
#include "glframework/texture.h"
/* *┌────────────────────────────────────────────────┐ *│ 目 标: 学习初步使用ViewMatrix *│ 讲 师: 赵新政(Carma Zhao) *│ 拆分目标: * -1 学习初步使用lookAt函数 * -2 将viewMatrix加入vertexShader * -3 测试viewMatrix *└────────────────────────────────────────────────┘ */
GLuint vao;
Shader* shader = nullptr;
Texture* texture = nullptr;
glm::mat4 transform(1.0f);
glm::mat4 viewMatrix(1.0f);
void OnResize(int width, int height) {
GL_CALL(glViewport(0, 0, width, height));
std::cout << "OnResize" << std::endl;
}
void OnKey(int key, int action, int mods) {
std::cout << key << std::endl;
}
void prepareVAO() {
//1 准备positions colors
float positions[] = {
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f,
};
float colors[] = {
1.0f, 0.0f,0.0f,
0.0f, 1.0f,0.0f,
0.0f, 0.0f,1.0f,
};
float uvs[] = {
0.0f, 0.0f,
1.0f, 0.0f,
0.5f, 1.0f,
};
unsigned int indices[] = {
0, 1, 2,
};
//2 VBO创建
GLuint posVbo, colorVbo, uvVbo;
glGenBuffers(1, &posVbo);
glBindBuffer(GL_ARRAY_BUFFER, posVbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STATIC_DRAW);
glGenBuffers(1, &colorVbo);
glBindBuffer(GL_ARRAY_BUFFER, colorVbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW);
glGenBuffers(1, &uvVbo);
glBindBuffer(GL_ARRAY_BUFFER, uvVbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(uvs), uvs, GL_STATIC_DRAW);
//3 EBO创建
GLuint ebo;
glGenBuffers(1, &ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
//4 VAO创建
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
//5 绑定vbo ebo 加入属性描述信息
//5.1 加入位置属性描述信息
glBindBuffer(GL_ARRAY_BUFFER, posVbo);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 3, (void*)0);
//5.2 加入颜色属性描述数据
glBindBuffer(GL_ARRAY_BUFFER, colorVbo);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 3, (void*)0);
//5.3 加入uv属性描述数据
glBindBuffer(GL_ARRAY_BUFFER, uvVbo);
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, (void*)0);
//5.4 加入ebo到当前的vao
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBindVertexArray(0);
}
void prepareShader() {
shader = new Shader("assets/shaders/vertex.glsl","assets/shaders/fragment.glsl");
}
void prepareTexture() {
texture = new Texture("assets/textures/goku.jpg", 0);
}
void prepareCamera() {
//lookat:生成一个viewMatrix
//eye:当前摄像机所在的位置
//center:当前摄像机看向的那个点
//up:穹顶向量
viewMatrix = glm::lookAt(glm::vec3(0.5f,0.0f,0.5f),glm::vec3(0.5f,0.0f,0.0f),glm::vec3(0.0f,1.0f,0.0f));
}
void render() {
//执行opengl画布清理操作
GL_CALL(glClear(GL_COLOR_BUFFER_BIT));
//绑定当前的program
shader->begin();
shader->setInt("sampler", 0);
shader->setMatrix4x4("transform", transform);
shader->setMatrix4x4("viewMatrix", viewMatrix);
//绑定当前的vao
GL_CALL(glBindVertexArray(vao));
//发出绘制指令
GL_CALL(glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0));
GL_CALL(glBindVertexArray(0));
shader->end();
}
int main() {
if (!app->init(800, 600)) {
return -1;
}
app->setResizeCallback(OnResize);
app->setKeyBoardCallback(OnKey);
//设置opengl视口以及清理颜色
GL_CALL(glViewport(0, 0, 800, 600));
GL_CALL(glClearColor(0.2f, 0.3f, 0.3f, 1.0f));
prepareShader();
prepareVAO();
prepareTexture();
prepareCamera();
while (app->update()) {
render();
}
app->destroy();
return 0;
}
文章评论