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 摄像机视角详解的更多相关文章

  1. OpenGL一些函数详解(二)

    OpenGL ES顶点数据绘制技巧 在OpenGL中,绘制一个长方体,需要将每个顶点的坐标放在一个数组中.保存坐标时有一些技巧(由于字母下标不好表示,因此将下标表示为单引号,如A1将在后文中表示为A' ...

  2. # OpenGL常用函数详解(持续更新)

    OpenGL常用函数详解(持续更新) 初始化 void glutInit(int* argc,char** argv)初始化GULT库,对应main函数的两个参数 void gultInitWindo ...

  3. OpenGL ES入门详解

     http://blog.csdn.net/wangyuchun_799/article/details/7736928 1.决定你要支持的OpenGL ES的版本.目前,OpenGL ES包含1.1 ...

  4. Qt的Graphics-View框架和OpenGL结合详解

    Qt的Graphics-View框架和OpenGL结合详解 演示程序下载地址:这里 程序源代码下载地址:这里 这是一篇纯技术文,介绍了这一个月来我抽时间研究的成果. Qt中有一个非常炫的例子:Boxe ...

  5. 一文详解 OpenGL ES 3.x 渲染管线

    OpenGL ES 构建的三维空间,其中的三维实体由许多的三角形拼接构成.如下图左侧所示的三维实体圆锥,其由许多三角形按照一定规律拼接构成.而组成圆锥的每一个三角形,其任意一个顶点由三维空间中 x.y ...

  6. OpenGL的glTranslatef平移变换函数详解

    OpenGL的glTranslatef平移变换函数详解 glTranslated()和glTranslatef()这两个函数是定义一个平移矩阵,该矩阵与当前矩阵相乘,使后续的图形进行平移变换. 我们先 ...

  7. OpenGL的glRotatef旋转变换函数详解

    OpenGL的glRotatef旋转变换函数详解 先看一下函数定义:void glRotatef(GLfloat angle,  GLfloat x,     GLfloat y,     GLflo ...

  8. OpenGL ES一些函数详解(一)

    glLoadIdentity和glMultMatrix   glLoadIdentity的作用是将当前模型视图矩阵转换为单位矩阵(行数和列数相同的矩阵,并且矩阵的左上角至右下角的连线上的元素都为1,其 ...

  9. 【OpenGL】详解第一个OpenGL程序

    写在前面 OpenGL能做的事情太多了!很多程序也看起来很复杂.很多人感觉OpenGL晦涩难懂,原因大多是被OpenGL里面各种语句搞得头大,一会gen一下,一会bind一下,一会又active一下. ...

  10. EasyNVR摄像机无插件直播进行摄像机云台控制的接入及调用详解

    EasyNVR云台接入及控制详解 摄像机云台控制在摄像机当中很常见摄像机能将当前状态下云台的水平角度.倾斜角度和摄像机镜头焦距等位置参数存储到设备中,需要时可以迅速调用这些参数并将云台和摄像头调整至该 ...

随机推荐

  1. 智能制造之路—从0开始打造一套轻量级MOM平台之基础平台搭建(Linux部署)

    一.前言 前面我们选定了Admin.net来搭建我们的MOM快速开发平台,本章主要描述.NET6平台的Linux部署,以及记录搭建过程中坑. 本次搭建我们选择某云的轻量应用服务器,系统选择CentOS ...

  2. 洛谷 Luogu P1038 [NOIP2003 提高组] 神经网络

    这题看着很吓人实则很简单.求输出层,正着求很麻烦,因为知不道谁连向这个点,所以可以反向建边,反着求. 拓扑+dfs,时间复杂度 \(\text{O(n + m)}\) #include <ios ...

  3. pandas去重方法

    原文链接:https://blog.csdn.net/missyougoon/article/details/83926840 数据去重可以使用duplicated()和drop_duplicates ...

  4. SpringBoot3集成Kafka

    目录 一.简介 二.环境搭建 1.Kafka部署 2.Kafka测试 3.可视化工具 三.工程搭建 1.工程结构 2.依赖管理 3.配置文件 四.基础用法 1.消息生产 2.消息消费 五.参考源码 标 ...

  5. 【pandas小技巧】--category类型补充

    category类型在pandas基础系列中有一篇介绍数据类型的文章中已经介绍过.category类型并不是python中的类型,是pandas特有的类型. category类型的优势那篇文章已经介绍 ...

  6. 5、Spring之bean的作用域和生命周期

    5.1.bean的作用域 5.1.1.单例(默认且常用) 5.1.1.1.配置bean 注意:当bean不配置scope属性时,默认是singleton(单例) <?xml version=&q ...

  7. 探索API接口:从概念到实践

    在当今数字化时代,API(Application Programming Interface)接口成为了各种应用程序之间实现数据交互和功能集成的关键.无论是开发一个网站.构建一个移动应用还是进行数据分 ...

  8. CodeForces 1343E Weights Distributing

    题意 多组样例 给定\(n,m,a,b,c\),给定一个长度为\(m\)的数组\(p[]\),给定\(m\)条边,构成一个\(n\)个点\(m\)条边的无向图,\(Mike\)想要从\(a\)走到\( ...

  9. 手机用户的开源福音「GitHub 热点速览」

    不知道多少用安卓机的小伙伴,被开屏广告烦过.相比有些克制的 iOS 机,安卓机是个应用基本上都有开屏广告,少则 3s 多则 10s,本周获得 1k+ star 的 Android-Touch-Help ...

  10. mysql 管理员常用命令

    1.创建用户 create user admin@localhost identified by 'password'; 2.赋权 grant privileges ON database.table ...