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)
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/738641.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

20240309web前端_第一周作业_豆瓣电影

作业四&#xff1a;豆瓣电影 成果展示&#xff1a; 完整代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0…

探索计算机视觉的未来

目录 前言1 计算机视觉简介2 计算机视觉的基本原理2.1 图像获取2.2 图像预处理2.3 特征提取2.4 模式识别 3 深度学习与计算机视觉3.1 深度学习的基本原理3.2 深度学习在计算机视觉中的应用 4 计算机视觉的应用领域4.1 人脸识别4.2 物体识别4.3 图像分割4.4 视频追踪 5 未来发展…

Midjourney从入门到实战:图像生成命令及参数详解

目录 0 专栏介绍1 Midjourney Bot常用命令2 Midjourney绘图指令格式3 Midjourney绘图指令参数3.1 模型及版本3.2 画面比例3.3 风格化3.4 图片质量3.5 混乱值3.6 随机数种子3.7 重复贴图3.8 停止3.8 垫图权重3.9 提示词权重分割 0 专栏介绍 &#x1f525;Midjourney是目前主流的…

Vue3全家桶 - VueRouter - 【3】嵌套路由【children】

嵌套路由【children】 如果在路由视图中展示的组件包含自己的路由占位符&#xff08;路由出口&#xff09;&#xff0c;则此处会用到嵌套路由&#xff1b;如图所示&#xff1a;点击关于链接&#xff0c;则会展示About组件&#xff0c;在其组件中又包含了路由链接和路由占位符&…

蓝桥杯-ISBN号码

此题然让本人纠结了很久&#xff0c;真的好多坑。。。。果然还是太菜了。 完整代码以及思路解析(在注释中) #include <iostream> using namespace std; int main() {string num;cin>>num; int count0;int w1;for(int i0;i<10;i){if((i!1)&&(i!5)) //坑…

常见的限流算法- python版本

shigen坚持更新文章的博客写手&#xff0c;擅长Java、python、vue、shell等编程语言和各种应用程序、脚本的开发。记录成长&#xff0c;分享认知&#xff0c;留住感动。 个人IP&#xff1a;shigen 在系统的稳定性设计中&#xff0c;需要考虑到的就是限流&#xff0c;避免高并发…

Elastic Stack--08--SpringData框架

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 SpringData[官网&#xff1a; https://spring.io/projects/spring-data](https://spring.io/projects/spring-data) Spring Data Elasticsearch 介绍 1.SpringData-…

AI+X 高校行:首场浙大站爆满!

Datawhale线下 线下活动&#xff1a;AIX 高校行活动 AIX&#xff1a;希望将人工智能&#xff08;AI&#xff09;与各个学科和行业&#xff08;X&#xff09;结合&#xff0c; 激发无限潜力和创造力&#xff08;X&#xff09;&#xff0c;让年轻人拥有更多可能性&#xff08;X&…

Discord OAuth2授权以及机器人监听群事件

下面文章讲解获取OAuth2授权整个流程&#xff0c;创建机器人&#xff0c;使用机器人监听工会&#xff08;工会就是创建的服务器&#xff09;成员变化等等&#xff0c;对接国外的都是需要VPN的哦&#xff0c;对接的时候记得提前准备。 创建应用 点击 此页面添加应用,&#xff…

Midjourney绘图欣赏系列(七)

Midjourney介绍 Midjourney 是生成式人工智能的一个很好的例子&#xff0c;它根据文本提示创建图像。它与 Dall-E 和 Stable Diffusion 一起成为最流行的 AI 艺术创作工具之一。与竞争对手不同&#xff0c;Midjourney 是自筹资金且闭源的&#xff0c;因此确切了解其幕后内容尚不…

【wps】wps与office办公函数储备使用(结合了使用案例 持续更新)

【wps】wps与office办公函数储备使用(结合了使用案例 持续更新) 1、TODAY函数 返回当前电脑系统显示的日期 TODAY函数&#xff1a;表示返回当前电脑系统显示的日期。 公式用法&#xff1a;TODAY() 2、NOW函数 返回当前电脑系统显示的日期和时间 NOW函数&#xff1a;表示返…

蚂蚁链摩斯荣获“艾瑞保险业数字化卓越服务商“奖

近日&#xff0c;艾瑞咨询发布《2023年中国保险业数字化转型研究报告》&#xff0c;摩斯隐私计算解决方案被报告入选&#xff0c;并获得“保险业数字化卓越服务商”奖。 蚂蚁摩斯是隐私计算行业的领先布局者&#xff1a;早在2017年&#xff0c;蚂蚁集团启动了隐私计算项目&…

Linux操作系统-07-Linux安装应用

一、使用rpm安装应用&#xff08;不推荐&#xff09; 先下载到本地&#xff0c;以.rpm文件名结尾&#xff0c;下载完成后&#xff0c;再安装 rpm -qa | grep mysql #查询当前系统是否有下载过mysql包 先上传mysql的rpm安装包到linux的opt目录 安装 rpm -ivh …

Linux 多进程开发(上)

第二章 Linux 多进程开发 2.1 进程概述2.2 进程状态转换2.3 进程创建2.4 exec 函数族2.5 进程控制 网络编程系列文章&#xff1a; 第1章 Linux系统编程入门&#xff08;上&#xff09; 第1章 Linux系统编程入门&#xff08;下&#xff09; 第2章 Linux多进程开发&#xff08;…

Opencv 插值方法 总结

一、概括 面试的时候问到了一个图&#xff0c;就是如何将一个算子放缩&#xff1f;&#xff1f;我第一反应是resize&#xff08;&#xff09;,但是后来我转念一想&#xff0c;人家问的是插值方式&#xff0c;今天来总结一下 最邻近插值法原理分析及c实现_最临近插值法-CSDN博…

Python与C++的对比——跟老吕学Python编程

Python与C的对比——跟老吕学Python编程 Python与C的对比1.C编译型 vs Python解释型2.执行效率3.开发效率4.跨平台5.可移植性6.内存管理机制7.易学性8.静态类型 vs 动态类型9.面向对象编程概念10.垃圾回收11.应用领域 Python与C的对比表 Python与C的对比 Python和C都是最受欢迎…

数据结构小记【Python/C++版】——散列表篇

一&#xff0c;基础概念 散列表&#xff0c;英文名是hash table&#xff0c;又叫哈希表。 散列表通常使用顺序表来存储集合元素&#xff0c;集合元素以一种很分散的分布方式存储在顺序表中。 散列表是一个键值对(key-item)的组合&#xff0c;由键(key)和元素值(item)组成。键…

解密阿里巴巴面试题:如何设计一个微博?

亲爱的小米科技粉丝们,大家好呀!今天小米带来了一则热门话题——阿里巴巴面试题:如何设计一个微博?别着急,跟着小米一起来揭秘吧! 实现哪些功能? 在设计微博系统时,需要考虑实现哪些功能才能满足用户的需求。除了基本的发布推文、时间线、新闻推送、关注/不允许用户以…

【JavaScript 漫游】【034】AJAX

文章简介 本篇文章为【JavaScript 漫游】专栏的第 034 篇文章&#xff0c;对浏览器模型的 XMLHttpRequest 对象&#xff08;AJAX&#xff09;的知识点进行了总结。 XMLHttpRequest 对象概述 浏览器与服务器之间&#xff0c;采用 HTTP 协议通信。用户在浏览器地址栏键入一个网…