OpenGL 摄像机视角详解
1. 摄像机
摄像机就好像是我们的眼睛,我们从摄像机的方向观察世界空间中的模型。摄像机远离模型,模型自然就变小了(透视投影下),然而,在GL中事实上并没有摄像机的概念。但是我们可以通过移动世界空间远离我们的摄像机来模拟摄像机远离世界的感觉
。这也正是在上一章中,我们的观察矩阵是(0,0,-3)的原因。我们通过将世界矩阵向屏幕里移动3个单位模拟摄像机向屏幕外移动三个单位。
上一章中,我们只是很简单的设置了一个观察矩阵,这一章,我们来仔细说一下如何设置这个观察矩阵,也就是,如何确定摄像机所在的坐标系。
2. 视察矩阵
事实上我们所要做的事就是确定一个观察矩阵,就可以完成从世界空间到观察空间的转换。而这在三维空间内,实际就是确定观察空间坐标系相对于世界空间坐标系的位置及各轴的夹角即可
。如果你空间感足够好的话,或者相对位置足够简单的话,你可能很快的就可以写出观察坐标系的表达式。但情况比较复杂的时候你可能需要耗费大量时间去完成这个转换,下面,我将尝试用一个通俗易懂的方式来介绍一下如何去生成这个坐标。
3. 摄像机的位置
这个很好理解,比如你规定了地面上一个物体作为世界的原点,那么随着你眼睛相对远点的位置改变时,你所看到的物体的样子也会随之改变。所以眼睛相对于远点的位置会直接影响物体的样子,同理,我们也需要确定摄像机相对于世界空间坐标原点的位置
。这里你可以看下上面图中的第一个图片来理解。坐标系就是世界空间,我们要确定的就是摄像机在世界空间中的位置。
4. 摄像机的方向
同样,即是你站在一个位置不动,你将目光集中在物体上不同的点时,你所看到的物体也不同。或者说,你目光的方向改变时,物体也跟着改变。同理,我们还需要确定摄像机观察的方向
。第二张图中就显示了摄像机在当前位置看向世界空间远点的示例。
5. 摄像机的滚转角
好了,现在你站在一个位置不动,目光也一直盯着物体的中心点不动,你还可以让你看到的物体改变。除了闭眼睛,你还可以歪一下头,你看到的东西是不是斜过来了(你非说没变那是因为强大的大脑已经帮你转换回来了又,你可以把眼睛换成手机摄像头然后倾斜手机再看看,手动滑稽)。所以,我们要确定摄像机在世界空间中摆放的夹角
。这么表述可能不清楚,稍微借一点坐标系的概念。我们摄像机的方向就是观察坐标系的Z轴。但是一个Z轴确定却并不能确定一个坐标系,我们至少要确定两个坐标轴,才能通过两个坐标轴确定第三个坐标轴从而建立一个坐标系。第四张图就显示了确定三个轴夹角后的坐标系。
6. 视察坐标系
经过上面的论述,我们知道了,我们只要知道摄像机的位置
、摄像机的方向
和x或y轴中任意一个轴的方向
即可确定。
如果我们不改变滚转角保证了摄像机坐标系的x轴与世界坐标系的y轴总是空间垂直,我们就可以通过摄像机方向向量与世界坐标系的y轴的方向向量叉乘从而获得摄像机坐标系的x轴
(两向量叉乘将获得同时垂直于两个向量的第三个向量)。
Look At
我们已经知道如何去构建一个摄像机坐标系了,不过怎么通过这些元素构建出观察矩阵呢?glm为我们提供了LookAt(position,target,up)函数。它含有三个参数:
- position,第一个参数就是我们摄像机在世界坐标系的位置了
- target,第二个参数是我们观察的点的位置,就是我们目光汇聚的那个点了,为什么是目标点呢?因为通过position减去target我们就可以获得摄像机方向的向量了
- up,第三个参数是一个与摄像机坐标系x轴垂直的向量。为什么是这个向量呢?因为我们可以通过position和target确定摄像机的方向,也就是摄像机坐标系z轴。再找到一个也与x轴垂直的向量即可确定x轴的方向向量了。
所以,上一章中我们生成的观察矩阵可以通过lookAt函数这样生成:
上面函数中,描述了我们的摄像机在世界坐标系的(0,0,3)位置,我们观察的点就是世界坐标原点,这个up向量就是世界坐标系的y轴的方向向量。
7. 圆周运动
那么现在,我们将我们的target保持在(0,0,0)这个点上,通过改变position来改变我们的lookAt矩阵。我们可以大概猜想一下结果就应该是我们围着一个东西转圈一直盯着这个东西的样子。
下面这段代码在渲染循环中:
8. 水平运动
接下来我们来模拟一下我们日常生活中的视角。我们想一下,一定是我们相对世界的position一直是改变的,我们眼睛的焦距是不变的,始终看我们自身位置前的某一个位置。
那么lookAt函数变成了这样:
那么现在我们还是只要改变我们的position就好了。
这里我们用键盘来接收我们想要做的移动的输入,代码如下:
上述左右移动时,我们看到我们用front向量与up向量相乘后标准化获取了right向量。这里之所以直接使用世界坐标系中的front向量和up向量,是因为我们当前观察角度的摄像机坐标系与世界坐标是的各轴完全是平行的,只是原点不同而已。
这样,我们就在这个世界中可以前后左右自由移动了。
9. 移动速度
我们看到,我们处理键盘输入是在渲染循环中处理的。试想如果我们的渲染循环循环一次的事件长,那么我们改变position经过的时间间隔就长,反之就短。那么如果一次渲染循环的时间是在一个范围内浮动的,那么物体运动的速度看起来也就是一个浮动的过程,我们应该监测每次渲染循环(更准确的应该是每次键盘事件处理)的时间间隔,通过这个时间间隔决定我们这一次position改变的数量
。
这里我们只要将我们之前定义的摄像机的速度在乘上一个时间间隔系数即可:
10. 视角移动
目前为止,我们实现了在世界中水平自由移动了,但是我们还不能转头也不能抬头。我们只要改变我们观察点的位置即可。这里,为了保证我们的焦距是不变的,所以我们要将front向量标准化。
现在我们的摄像机坐标系与世界坐标系的各轴是平行的。我们想抬头呢,我们就以x轴旋转坐标系,想左右牛头就以y轴旋转坐标系即可。
11. 欧拉角
我们先来看一下坐标系旋转角的概念和图示。
欧拉角(Euler Angle)是可以表示3D空间中任何旋转的3个值,由莱昂哈德·欧拉(Leonhard Euler)在18世纪提出。一共有3种欧拉角:俯仰角(Pitch)、偏航角(Yaw)和滚转角(Roll)。
俯仰角
是描述我们如何往上或往下看的角,可以在第一张图中看到。第二张图展示了偏航角
,偏航角表示我们往左和往右看的程度。滚转角
代表我们如何翻滚摄像机,通常在太空飞船的摄像机中使用。每个欧拉角都有一个值来表示,把三个角结合起来我们就能够计算3D空间中任何的旋转向量了。
我们来看一下如何计算这个向量。
12. 鼠标输入
首先,我们要告诉GL,如果捕捉光标的话,我们不应该展示光标(当然你也可以展示)。
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
然后我们应该设置鼠标事件的回调:
glfwSetCursorPosCallback(window, mouse_callback);
接下来就是回调函数了:
13. 缩放
目前为止,我们可以自由在世界中移动,改变视角了已经。但是我们还想给摄像机添加一个缩放功能,你可能说我走近点东西不就大了么。的确是这样的,不过我们也可以在原地做到这件事。我们可以利用透视的特性完成这件事
。在同样的距离观察一个物体,视野越小时,我们能观察到的物体的部分越小,但是我们的屏幕是不变的,也就是屏幕上显示的物体的部分越小,这样就好像物体被放大了一样。所以我们只要改动这个fov值就可以完成缩放。
OpenGL 摄像机视角详解的更多相关文章
- OpenGL一些函数详解(二)
OpenGL ES顶点数据绘制技巧 在OpenGL中,绘制一个长方体,需要将每个顶点的坐标放在一个数组中.保存坐标时有一些技巧(由于字母下标不好表示,因此将下标表示为单引号,如A1将在后文中表示为A' ...
- # OpenGL常用函数详解(持续更新)
OpenGL常用函数详解(持续更新) 初始化 void glutInit(int* argc,char** argv)初始化GULT库,对应main函数的两个参数 void gultInitWindo ...
- OpenGL ES入门详解
http://blog.csdn.net/wangyuchun_799/article/details/7736928 1.决定你要支持的OpenGL ES的版本.目前,OpenGL ES包含1.1 ...
- Qt的Graphics-View框架和OpenGL结合详解
Qt的Graphics-View框架和OpenGL结合详解 演示程序下载地址:这里 程序源代码下载地址:这里 这是一篇纯技术文,介绍了这一个月来我抽时间研究的成果. Qt中有一个非常炫的例子:Boxe ...
- 一文详解 OpenGL ES 3.x 渲染管线
OpenGL ES 构建的三维空间,其中的三维实体由许多的三角形拼接构成.如下图左侧所示的三维实体圆锥,其由许多三角形按照一定规律拼接构成.而组成圆锥的每一个三角形,其任意一个顶点由三维空间中 x.y ...
- OpenGL的glTranslatef平移变换函数详解
OpenGL的glTranslatef平移变换函数详解 glTranslated()和glTranslatef()这两个函数是定义一个平移矩阵,该矩阵与当前矩阵相乘,使后续的图形进行平移变换. 我们先 ...
- OpenGL的glRotatef旋转变换函数详解
OpenGL的glRotatef旋转变换函数详解 先看一下函数定义:void glRotatef(GLfloat angle, GLfloat x, GLfloat y, GLflo ...
- OpenGL ES一些函数详解(一)
glLoadIdentity和glMultMatrix glLoadIdentity的作用是将当前模型视图矩阵转换为单位矩阵(行数和列数相同的矩阵,并且矩阵的左上角至右下角的连线上的元素都为1,其 ...
- 【OpenGL】详解第一个OpenGL程序
写在前面 OpenGL能做的事情太多了!很多程序也看起来很复杂.很多人感觉OpenGL晦涩难懂,原因大多是被OpenGL里面各种语句搞得头大,一会gen一下,一会bind一下,一会又active一下. ...
- EasyNVR摄像机无插件直播进行摄像机云台控制的接入及调用详解
EasyNVR云台接入及控制详解 摄像机云台控制在摄像机当中很常见摄像机能将当前状态下云台的水平角度.倾斜角度和摄像机镜头焦距等位置参数存储到设备中,需要时可以迅速调用这些参数并将云台和摄像头调整至该 ...
随机推荐
- Win32API中的宽字符
4.1了解什么是Win32API Win32API就是windows操作系统提供给我们的函数(应用程序接口),其主要存放在C:\Windows\System32 (存储的DLL是64位).C:\Win ...
- AI视频风格转换:Stable Diffusion+EBSynth
这次转换的视频还比较稳定,先给大家看下效果. 这里传不了视频,还是放到盘盘了:https://www.aliyundrive.com/s/5mzfjLViyDa 书接上文,在上一篇文章中,我们首先使用 ...
- 源码解析Collections.sort ——从一个逃过单测的 bug 说起
本文从一个小明写的bug 开始,讲bug的发现.排查定位,并由此展开对涉及的算法进行图解分析和源码分析. 事情挺曲折的,因为小明的代码是有单测的,让小明更加笃定自己写的没问题.所以在排查的时候,也经历 ...
- N1nEmAn-2023集训wp
week1 2022-12-28 WP 0x00 T1 reverse3 前几天没注意到要发wp,现在补一下.最近在学汇编,pwn题没做新的了.想到之前了解到hws的pwn会考花指令,听hjx他们说那 ...
- quarkus数据库篇之四:本地缓存
欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇概览 本文是<quarkus数据库篇> ...
- AI绘画关键词Prompt:分享一些质量比较高的StableDiffusion(SD)关键词网站
今天向大家推荐一些SD(StableDiffusion)高质量的 关键词 网站.这些网站的质量可靠,能为大家在创建 AI 绘画时提供有效的参考.以下是六个推荐的网站,优缺点分析. 有几个质量还算是挺高 ...
- Linux查看磁盘空间,文件系统、挂载
Linux磁盘空间,文件系统.挂载 概述 在使用以下命令查看磁盘使用情况时 df -h du -sh 目标路径 作为初级开发者,Linux入门级选手,可能不禁要问Linux系统的文件系统跟window ...
- 浅谈API安全的应用
理论基础 API它的全称是Application Programming Interface,也叫做应用程序接口,它定义了软件之间的数据交互方式.功能类型.随着互联网的普及和发展,API 从早期的 ...
- JWT(Json Wen Token)原理剖析
JWT(即json web token),大家先看下面这张图 大家可以观察到,jwt String就是生成后的jwt字符集,其中有两个 "."(注意:jwt校验会对".& ...
- 【matplotlib基础】--图例
Matplotlib 中的图例是帮助观察者理解图像数据的重要工具.图例通常包含在图像中,用于解释不同的颜色.形状.标签和其他元素. 1. 主要参数 当不设置图例的参数时,默认的图例是这样的. impo ...