OpenGL笔记十六之视图变换矩阵实验-lookAt函数

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 &center,
    glm::vec3 const &up
);

参数详解

  1. eye: 摄像机的位置,即视点 (View Position)。
  2. center: 摄像机观察的目标位置,即目标点 (Target Position)。
  3. up: 定义摄像机的上方向 (Up Vector)。

返回值

glm::lookAt 返回一个 glm::mat4 类型的 4x4 视图矩阵。

工作原理

glm::lookAt 函数通过以下几个步骤生成视图矩阵:

  1. 定义摄像机的坐标系

    • Z 轴 (Forward Vector): 计算方向向量 f,即从 eye 指向 center 的向量,并归一化。
    • X 轴 (Right Vector): 计算右方向向量 s,即 fup 的叉积,并归一化。
    • Y 轴 (True Up Vector): 计算新的上方向向量 u,即 sf 的叉积。
  2. 构建旋转矩阵
    通过 su-f 生成旋转矩阵。

  3. 构建平移矩阵
    通过 eye 的负值生成平移矩阵。

  4. 组合变换矩阵
    将旋转矩阵和平移矩阵相乘,得到最终的视图矩阵。

视图矩阵计算的过程

glm::lookAt 函数生成的视图矩阵的输出依赖于传递的参数。假设摄像机的位置 (eye) 为 (0.0f, 0.0f, 5.0f),目标位置 (center) 为 (0.0f, 0.0f, 0.0f),上方向 (up) 为 (0.0f, 1.0f, 0.0f)。可以手动计算出视图矩阵,或者直接通过代码输出。这是手动计算的过程:

  1. 计算方向向量 (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)
    
  2. 计算右方向向量 (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)
    
  3. 计算新的上方向向量 (True Up Vector) u

    glm::vec3 u = glm::cross(s, f);
    // u = cross((1, 0, 0), (0, 0, -1)) = (0, 1, 0)
    
  4. 构建旋转矩阵

    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 ]
    
  5. 构建平移矩阵

    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 ]
    
  6. 组合变换矩阵

    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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只野生的善逸

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值