Learn OpenGL 07 摄像机
定义摄像机参数
glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 3.0f);//摄像机位置glm::vec3 cameraTarget = glm::vec3(0.0f, 0.0f, 0.0f);glm::vec3 cameraDirection = glm::normalize(cameraPos - cameraTarget);//摄像机方向,指向z轴正方向 glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f);glm::vec3 cameraRight = glm::normalize(glm::cross(up, cameraDirection));//摄像机右方向glm::vec3 cameraUp = glm::cross(cameraDirection, cameraRight);//上方向
构建View矩阵
这里可以参考Games101的MVP变换那一节
视图变换要做的内容就是把相机放到正确的位置和角度上,但其实变换矩阵都是对模型进行变换,因为如果对模型进行视图变换以后,相机也就在正确的位置上了。

这里就是相机移动到原点,g指向-z,t指向y等等。因为正着构建矩阵很困难,所以可以先求矩阵的逆变换。
分别将X(1,0,0,0),Y,Z轴带入可以发现都符合

也就是这样
glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 3.0f);//摄像机位置glm::vec3 cameraTarget = glm::vec3(0.0f, 0.0f, 0.0f);glm::vec3 cameraDirection = glm::normalize(cameraPos - cameraTarget);//摄像机方向,指向z轴正方向 glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f);glm::vec3 cameraRight = glm::normalize(glm::cross(up, cameraDirection));//摄像机右方向glm::vec3 cameraUp = glm::cross(cameraDirection, cameraRight);//上方向glm::mat4 view;view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);
自由移动摄像机
需要在按键检测函数里加上对应按键发生的时候对应的函数
float cameraSpeed = 0.05f; // adjust accordinglyif (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)cameraPos += cameraSpeed * cameraFront;if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)cameraPos -= cameraSpeed * cameraFront;if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
转换欧拉角
对于我们的摄像机系统来说,我们只关心俯仰角和偏航角,所以我们不会讨论滚转角。给定一个俯仰角和偏航角,我们可以把它们转换为一个代表新的方向向量的3D向量。

如果我们想象自己在xz平面上,看向y轴,我们可以基于第一个三角形计算来计算它的长度/y方向的强度(Strength)(我们往上或往下看多少)。从图中我们可以看到对于一个给定俯仰角的y值等于sin θ:
direction.y = sin(glm::radians(pitch)); // 注意我们先把角度转为弧度
这里我们只更新了y值,仔细观察x和z分量也被影响了。从三角形中我们可以看到它们的值等于:
direction.x = cos(glm::radians(pitch));
direction.z = cos(glm::radians(pitch));
看看我们是否能够为偏航角找到需要的分量:

就像俯仰角的三角形一样,我们可以看到x分量取决于cos(yaw)的值,z值同样取决于偏航角的正弦值。把这个加到前面的值中,会得到基于俯仰角和偏航角的方向向量:
direction.x = cos(glm::radians(pitch)) * cos(glm::radians(yaw)); // 译注:direction代表摄像机的前轴(Front),这个前轴是和本文第一幅图片的第二个摄像机的方向向量是相反的
direction.y = sin(glm::radians(pitch));
direction.z = cos(glm::radians(pitch)) * sin(glm::radians(yaw));
这样我们就有了一个可以把俯仰角和偏航角转化为用来自由旋转视角的摄像机的3维方向向量了。
鼠标输入与缩放
分别设置鼠标移动和滚轮滑动的回调函数就可以了
可以先设置这个函数隐藏光标
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);//隐藏光标
设置两个回调函数
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{if (firstMouse){lastX = xpos;lastY = ypos;firstMouse = false;}float xoffset = xpos - lastX;float yoffset = lastY - ypos;lastX = xpos;lastY = ypos;float sensitivity = 0.03;xoffset *= sensitivity;yoffset *= sensitivity;yaw += xoffset;pitch += yoffset;if (pitch > 89.0f)pitch = 89.0f;if (pitch < -89.0f)pitch = -89.0f;glm::vec3 front;front.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch));front.y = sin(glm::radians(pitch));front.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch));cameraFront = glm::normalize(front);
}void scroll_callbacks(GLFWwindow* window, double xoffset, double yoffset)
{if (fov >= 1.0f && fov <= 45.0f)fov -= yoffset;if (fov <= 1.0f)fov = 1.0f;if (fov >= 45.0f)fov = 45.0f;
}
要到前面注册回调函数
glfwSetScrollCallback(window, scroll_callbacks);//注册回调函数glfwSetCursorPosCallback(window, mouse_callback);
实现摄像机类
因为每个场景都一定会需要一个摄像机,所以将摄像机类分离出来是个很有必要的事情,方便以后重复使用。直接创建一个摄像机类,它会自动生成一个camera.h文件和camera.cpp文件
camera.h
#pragma once#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <GLFW/glfw3.h>// Defines several possible options for camera movement. Used as abstraction to stay away from window-system specific input methods
enum Camera_Movement {FORWARD,BACKWARD,LEFT,RIGHT
};// Default camera values
const float YAW = -90.0f;
const float PITCH = 0.0f;
const float SPEED = 2.5f;
const float SENSITIVITY = 0.1f;
const float ZOOM = 45.0f;class Camera
{
public:// camera Attributesglm::vec3 Position; //摄影机位置glm::vec3 Front; //Forward 摄影机的“方向”(一个和朝向相反的向量)glm::vec3 Up; //摄影机的上方向glm::vec3 Right;glm::vec3 WorldUp; //世界的上方向// euler Anglesfloat Yaw;//偏航角float Pitch;//俯仰角// camera optionsfloat MovementSpeed;float MouseSensitivity;float Zoom;//FOV// constructor with vectorsCamera(glm::vec3 position = glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f), float yaw = YAW, float pitch = PITCH);Camera(float posX, float posY, float posZ, float upX, float upY, float upZ, float yaw, float pitch);glm::mat4 GetViewMatrix();void ProcessKeyboard(Camera_Movement direction, float deltaTime);//按键检测函数void ProcessMouseMovement(float xoffset, float yoffset, GLboolean constrainPitch = true);//鼠标移动检测函数void ProcessMouseScroll(float yoffset);//滚轮移动函数private:// calculates the front vector from the Camera's (updated) Euler Anglesvoid updateCameraVectors();//通过欧拉角更新摄像机位置};
Camera.cpp
#include "Camera.h"Camera::Camera(glm::vec3 position, glm::vec3 up, float yaw, float pitch): Front(glm::vec3(0.0f, 0.0f, -1.0f)), MovementSpeed(SPEED), MouseSensitivity(SENSITIVITY), Zoom(ZOOM)
{Position = position;WorldUp = up;Yaw = yaw;Pitch = pitch;updateCameraVectors();
}Camera::Camera(float posX, float posY, float posZ, float upX, float upY, float upZ, float yaw, float pitch): Front(glm::vec3(0.0f, 0.0f, -1.0f)), MovementSpeed(SPEED), MouseSensitivity(SENSITIVITY), Zoom(ZOOM)
{Position = glm::vec3(posX, posY, posZ);WorldUp = glm::vec3(upX, upY, upZ);Yaw = yaw;Pitch = pitch;updateCameraVectors();
}// returns the view matrix calculated using Euler Angles and the LookAt Matrix
glm::mat4 Camera::GetViewMatrix()
{return glm::lookAt(Position, Position + Front, Up);
}// processes input received from any keyboard-like input system. Accepts input parameter in the form of camera defined ENUM (to abstract it from windowing systems)
void Camera::ProcessKeyboard(Camera_Movement direction, float deltaTime)
{float velocity = MovementSpeed * deltaTime;if (direction == FORWARD)Position += Front * velocity;if (direction == BACKWARD)Position -= Front * velocity;if (direction == LEFT)Position -= Right * velocity;if (direction == RIGHT)Position += Right * velocity;
}// processes input received from a mouse input system. Expects the offset value in both the x and y direction.
void Camera::ProcessMouseMovement(float xoffset, float yoffset, GLboolean constrainPitch )
{xoffset *= MouseSensitivity;yoffset *= MouseSensitivity;Yaw += xoffset;Pitch += yoffset;// make sure that when pitch is out of bounds, screen doesn't get flippedif (constrainPitch){ if (Pitch > 89.0f)Pitch = 89.0f;if (Pitch < -89.0f)Pitch = -89.0f;}//std::cout<<// update Front, Right and Up Vectors using the updated Euler anglesupdateCameraVectors();
}// processes input received from a mouse scroll-wheel event. Only requires input on the vertical wheel-axis
void Camera::ProcessMouseScroll(float yoffset)
{Zoom -= (float)yoffset;if (Zoom < 1.0f)Zoom = 1.0f;if (Zoom > 45.0f)Zoom = 45.0f;
}void Camera::updateCameraVectors()
{// calculate the new Front vectorglm::vec3 front;front.x = cos(glm::radians(Yaw)) * cos(glm::radians(Pitch));front.y = sin(glm::radians(Pitch));front.z = sin(glm::radians(Yaw)) * cos(glm::radians(Pitch));Front = glm::normalize(front);// also re-calculate the Right and Up vectorRight = glm::normalize(glm::cross(Front, WorldUp)); // normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.Up = glm::normalize(glm::cross(Right, Front));
}
大功告成,注意在main函数里对应的变量也需要修改,然后main.cpp里原来声明的定义相机的变量就可以删掉了
练习
-
看看你是否能够修改摄像机类,使得其能够变成一个真正的FPS摄像机(也就是说不能够随意飞行);你只能够呆在xz平面上:参考解答
-
只需要最后将y值永远设置在平面上就可以
void Camera::ProcessKeyboard(Camera_Movement direction, float deltaTime)
{float velocity = MovementSpeed * deltaTime;if (direction == FORWARD)Position += Front * velocity;if (direction == BACKWARD)Position -= Front * velocity;if (direction == LEFT)Position -= Right * velocity;if (direction == RIGHT)Position += Right * velocity;Position.y = 0.0f;}
-
试着创建你自己的LookAt函数,其中你需要手动创建一个我们在一开始讨论的观察矩阵。用你的函数实现来替换GLM的LookAt函数,看看它是否还能一样地工作:参考解答
根据这张图把矩阵构建出来即可
注意GLM规定了矩阵的第一个参数是列,第二个参数是行
旋转矩阵的第一行是右向量,第二行是上面的向量,第三行是方向向量。
依次计算出向量搭建出矩阵就可以了
// Custom implementation of the LookAt function
glm::mat4 Camera::calculate_lookAt_matrix(glm::vec3 position, glm::vec3 target, glm::vec3 worldUp)
{// 1. Position = known// 2. Calculate cameraDirectionglm::vec3 zaxis = glm::normalize(position - target);// 3. Get positive right axis vectorglm::vec3 xaxis = glm::normalize(glm::cross(glm::normalize(worldUp), zaxis));// 4. Calculate camera up vectorglm::vec3 yaxis = glm::cross(zaxis, xaxis);// Create translation and rotation matrix// In glm we access elements as mat[col][row] due to column-major layoutglm::mat4 translation = glm::mat4(1.0f); // Identity matrix by defaulttranslation[3][0] = -position.x; // Third column, first rowtranslation[3][1] = -position.y;translation[3][2] = -position.z;glm::mat4 rotation = glm::mat4(1.0f);rotation[0][0] = xaxis.x; // First column, first rowrotation[1][0] = xaxis.y;rotation[2][0] = xaxis.z;rotation[0][1] = yaxis.x; // First column, second rowrotation[1][1] = yaxis.y;rotation[2][1] = yaxis.z;rotation[0][2] = zaxis.x; // First column, third rowrotation[1][2] = zaxis.y;rotation[2][2] = zaxis.z;// Return lookAt matrix as combination of translation and rotation matrixreturn rotation * translation; // Remember to read from right to left (first translation then rotation)
}
相关文章:
Learn OpenGL 07 摄像机
定义摄像机参数 glm::vec3 cameraPos glm::vec3(0.0f, 0.0f, 3.0f);//摄像机位置glm::vec3 cameraTarget glm::vec3(0.0f, 0.0f, 0.0f);glm::vec3 cameraDirection glm::normalize(cameraPos - cameraTarget);//摄像机方向,指向z轴正方向 glm::vec3 up glm::vec…...
Linux系统部署火狐浏览器结合内网穿透实现公网访问
目录 前言 1. 部署Firefox 2. 本地访问Firefox 3. Linux安装Cpolar 4. 配置Firefox公网地址 5. 远程访问Firefox 6. 固定Firefox公网地址 7. 固定地址访问Firefox 结语 前言 作者简介: 懒大王敲代码,计算机专业应届生 今天给大家聊聊Linux系统…...
Elastic Stack--05--聚合、映射mapping
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 1.聚合(aggregations)基本概念桶(bucket)度量(metrics) 案例 11. 接下来按price字段进行分组:2. 若想对所…...
【嵌入式——QT】Model/View
【嵌入式——QT】Model/View 基本原理数据模型视图组件代理Model/View结构的一些概念QFileSystemModelQStringListModelQStandardItemModel自定义代理 基本原理 GUI应用程序的一个很重要的功能是由用户在界面上编辑和修改数据,典型的如数据库应用程序,数…...
向量化编程书籍推荐
文章目录 1. 书籍清单 1. 书籍清单 《Linear Algebra and Its Applications》 by Gilbert Strang 这本书是线性代数的经典教材,线性代数是向量化编程的基础。它涵盖了向量、矩阵、线性变换等内容,对理解向量化编程的数学概念非常有帮助《NumPy Beginner…...
算法D43 | 动态规划5 | 1049. 最后一块石头的重量 II 494. 目标和 474.一和零
1049. 最后一块石头的重量 II 本题就和 昨天的 416. 分割等和子集 很像了,可以尝试先自己思考做一做。 视频讲解:动态规划之背包问题,这个背包最多能装多少?LeetCode:1049.最后一块石头的重量II_哔哩哔哩_bilibili 代…...
设计模式—桥接模式
定义: 桥接模式是将抽象部分与它的实现部分分离,使它们都可以独立地变化。它是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(Interfce)模式。 本章代码:小麻雀icknn/设计模式练习 - Gitee.com 结构: 抽象化(Abstraction)角色:…...
伊萨卡训练代码
我们建议创建并激活 conda 环境,以确保在下面安装正确的软件包版本的干净环境。 # Optional but recommended: conda create -n ithaca python3.9 conda activate ithaca 克隆此存储库并进入其根目录。通过以下方式安装完整的 ithaca 依赖项(包括训练&am…...
视频产品介绍:AS-VCVR-N多协议视频接入网关
目 录 一、产品概述 (一)非标设备接入 (二)信令流转换 (三)媒体流转发 二、网关特性 三、技术参数 一、产品概述 视频接入网关服务是终端用户与视频源的传输枢纽,实现把前端不同…...
大型网站架构演化总结
本文图解大型网站架构演化。 目录 1、单一应用服务阶段 2、应用与数据服务分离阶段 3、利用缓存提高性能阶段 4、应用服务集群阶段 5、数据库读写分离阶段 6、反向代理与CDN加速阶段 7、分布式数据库阶段 8、 NoSQL与搜索引擎阶段 9、业务拆分阶段 10、分布式服务阶…...
5G智能制造纺织工厂数字孪生可视化平台,推进纺织行业数字化转型
5G智能制造纺织工厂数字孪生可视化平台,推进纺织行业数字化转型。纺织工业作为传统制造业的重要组成部分,面临着转型升级的紧迫需求。随着5G技术的快速发展,智能制造成为纺织工业转型升级的重要方向。数字孪生可视化平台作为智能制造的核心技…...
仿牛客网项目---Elasticsearch分布式搜索引擎
1.什么是ElasticSearch分布式搜索引擎? Elasticsearch是一个开源的分布式搜索引擎,提供实时的、高可用性的搜索和分析解决方案。它支持快速索引和搜索大规模数据,具有分布式架构、RESTful API、基于JSON的查询语言等功能,适用于各…...
macbook pro 2018 安装 arch linux 双系统
文章目录 友情提醒关于我的 mac在 mac 上需要提前做的事情复制 wifi 驱动 在 linux 上的操作还原 wifi 驱动连接 wifi 网络磁盘分区制作文件系统挂载分区 使用 archinstall 来安装 arch linux遗留问题 友情提醒 安装 archl linux 的时候,mac 的键盘是没法用的&#…...
虚拟机安装CentOS教学,超详细一步安装到底!
首先将Centos的镜像文件进行下载,随后再进行安装配置: https://mirrors.tuna.tsinghua.edu.cn/centos-vault/7.8.2003/isos/x86_64/CentOS-7-x86_64-DVD-2003.iso 1.打开VMware,新建虚拟机,选择典型安装,点击下一步 2.选择稍…...
“2024杭州智慧城市及安防展会”将于4月在杭州博览中心盛大召开
2024杭州国际智慧城市及安防展览会,将于4月24日在杭州国际博览中心盛大开幕。这场备受瞩目的盛会,不仅汇集了全球智慧城市与安防领域的顶尖企业,更是展示最新技术、交流创新理念的重要平台。近日,从组委会传来消息,展会…...
【C++庖丁解牛】模拟实现STL的string容器(最后附源码)
📙 作者简介 :RO-BERRY 📗 学习方向:致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 📒 日后方向 : 偏向于CPP开发以及大数据方向,欢迎各位关注,谢谢各位的支持 目录 1.vs和g下string结构…...
不要在代码中随便使用try...catch了
前言 📫 大家好,我是南木元元,热爱技术和分享,欢迎大家交流,一起学习进步! 🍅 个人主页:南木元元 目录 背景 js中的try...catch try...catch运行机制 js的事件循环机制 try...c…...
网络编程(3/6)
使用C语言完成数据库的增删改 #include<myhead.h> int do_add(sqlite3 *ppDb) {int numb;char name[50];int salary;printf("请输入员工信息:工号、姓名、薪水\n");scanf("%d %s %d",&numb,name,&salary);char sql[128];char *e…...
(day 2)JavaScript学习笔记(基础之变量、常量和注释)
概述 这是我的学习笔记,记录了JavaScript的学习过程,我是有一些Python基础的,因此在学习的过程中不自觉的把JavaScript的代码跟Python代码做对比,以便加深印象。我本人学习软件开发纯属个人兴趣,大学所学的专业也非软件…...
Spring Boot中全局异常处理器
文章目录 1.Spring Boot中两种异常处理方式2.为什么需要全局异常处理呢?3. 全局异常处理器测试4.ControllerAdvice 详解5.ExceptionHandler 详解 1.Spring Boot中两种异常处理方式 要想解决测试中存在的问题,我们需要对程序中可能出现的异常进行捕获&am…...
React hook之useRef
React useRef 详解 useRef 是 React 提供的一个 Hook,用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途,下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...
2025年能源电力系统与流体力学国际会议 (EPSFD 2025)
2025年能源电力系统与流体力学国际会议(EPSFD 2025)将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会,EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...
【机器视觉】单目测距——运动结构恢复
ps:图是随便找的,为了凑个封面 前言 在前面对光流法进行进一步改进,希望将2D光流推广至3D场景流时,发现2D转3D过程中存在尺度歧义问题,需要补全摄像头拍摄图像中缺失的深度信息,否则解空间不收敛…...
电脑插入多块移动硬盘后经常出现卡顿和蓝屏
当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时,可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案: 1. 检查电源供电问题 问题原因:多块移动硬盘同时运行可能导致USB接口供电不足&#x…...
el-switch文字内置
el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...
2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面
代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口(适配服务端返回 Token) export const login async (code, avatar) > {const res await http…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...
三体问题详解
从物理学角度,三体问题之所以不稳定,是因为三个天体在万有引力作用下相互作用,形成一个非线性耦合系统。我们可以从牛顿经典力学出发,列出具体的运动方程,并说明为何这个系统本质上是混沌的,无法得到一般解…...
今日科技热点速览
🔥 今日科技热点速览 🎮 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售,主打更强图形性能与沉浸式体验,支持多模态交互,受到全球玩家热捧 。 🤖 人工智能持续突破 DeepSeek-R1&…...
智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制
在数字化浪潮席卷全球的今天,数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具,在大规模数据获取中发挥着关键作用。然而,传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时,常出现数据质…...
