OpenGL模型变换,视图变换矩阵推导过程与glm::LookAt源码解析

本文将详细介绍OpenGL中的几个坐标系的概念,常见的几种变换,以及视图变换矩阵的推导过程,最后会分析下glm库视图变换矩阵的源码

首先,模型变换(Model),视图变换(View),投影变换(Projection)就是我们常说的三个特殊的矩阵,也经常被统一称为MVP矩阵,要理解他们,我们首先要理解几个坐标系的概念。

右手坐标系

右手坐标系与左手坐标系都是三维笛卡尔坐标系,他们唯一的不同在于z轴的方向,如下图,左边是左手坐标系,右边是右手坐标系
在这里插入图片描述
OpenGL中一般用的是右手坐标系

下面的几个坐标系则是在图形的渲染管线中对顶点进行变换所用到的坐标系

1.模型坐标系(Local Space)

模型坐标系(或者叫本地坐标系、局部坐标系,本文后面统一称模型坐标系)顾名思义,就是以物体的正中心为原点的坐标系,通常,我们从三维软件中导出的模型基本是局部坐标系,以模型的中心为原点,其他顶点相对于模型的原点来定义。

但是我们观察一个物体不可能从物体的中心去看它,所以我们需要将它摆放到我们的世界中,因此引入了世界坐标系,而我们的模型变换就是将物体在模型坐标系中的点转换到世界坐标系中。

2. 世界坐标系 (World Space)

世界坐标系就是全局的那个坐标系,我们的物体,以及后面观察物体用到的摄像机全都在世界坐标系中

3. 视图坐标系(View Space)

视图坐标系(或者叫观察者坐标系,本文后面统一称视图坐标系)也就是在世界坐标系中通过假想一个摄像机或者观察者的存在,以摄像机的位置为中心原点,然后从摄像机这个观察者的角度去重新计算世界中的物体相对于摄像机原点的坐标

4. 裁剪坐标系(Clip Space)

不管从哪个角度分析,能观察到的内容都是有限的,可以抽象出一个视锥体代表所观察到的区域,观察到的内容会投影到一块最终的屏幕上,类似于影院最终放映的大屏幕,放映机就是我们观察者的所在的位置,投射出的光线就是我们的视锥体,在视锥体以内的图形最终会被映射到我们的标准化设备坐标系[-1, 1]中,视锥体以外的图形则会被裁减,最终不会出现在我们的屏幕中

坐标系之间的转换

在这里插入图片描述

模型变换

模型变换包含三个最基本的变换(平移、旋转、缩放),任何三位空间中位置的移动都可以由这三种变换组合而成

平移

先来看看平移,假设空间中一个物体的坐标为(x,y,z),让它沿x轴,y轴,z轴分别平移一段距离后的坐标为(x + x0, y + y0, z + z0),则由线性代数的知识可以得到
在这里插入图片描述
其中在这里插入图片描述就是我们的平移矩阵

旋转

假设固定一个坐标轴不变,将物体绕z轴旋转,则从z轴看过去,就可以简化为二维空间的旋转,推导过程如下
在这里插入图片描述
则二维平面中将点 (x0, y0) 绕原点逆时针旋转α角的旋转矩阵为
在这里插入图片描述
因此在三维空间中将一个物体沿着z轴逆时针旋转α角,就是z坐标不变,x和y逆时针旋转 α角,旋转矩阵为
在这里插入图片描述
同样的,沿着y轴逆时针旋转α角
在这里插入图片描述
沿着x轴逆时针旋转α角
在这里插入图片描述

缩放

缩放则比较简单,三维空间中将一个物体沿着三个轴分别缩放 sx, sy ,sz倍,则缩放矩阵为
在这里插入图片描述

视图变换

准备知识

在推导视图变换的矩阵前,我们得有一个先验知识,那就是旋转矩阵都是正交矩阵,正交矩阵拥有非常多不错的性质,比如

  • 正交矩阵的每一列都是单位矩阵,且两两相交
  • 正交矩阵的逆等于正交矩阵的转置,且正交的行列式必定为+1或-1

我们也可以简单的证明一下,比如原先物体绕z轴逆时针旋转α的旋转矩阵为在这里插入图片描述
则它的相反变换也就是绕z轴顺时针旋转α(逆时针旋转 -α )的旋转矩阵为
在这里插入图片描述
在这里插入图片描述就是原先的旋转矩阵在这里插入图片描述的转置,且两个矩阵的行列式都为 1,正交矩阵的性质在我么推导视图变换矩阵的时候特别有用

正式推导

要把世界坐标里的一个点转到视图坐标系里,我们不妨来看看怎么先把摄像机里的坐标转到世界坐标系里(因为世界坐标系的原点永远是 ( 0, 0, 0 ),然后求它的逆矩阵就好了
因此我们需要将摄像机从原来在世界坐标系中的位置位移到原点,然后看向屏幕的里面(z轴的负方向,OpenGL是右手坐标系),根据相对运动可知,如果物体也跟着相机做相同的变换,那么相机里看到的物体画面并不会发生任何改变,也就达到了我们想要的效果。
在这里插入图片描述
我们需要在摄像机所在的地方构建一个坐标系,首先指定一个摄像机的位置(原始摄像机坐标系的原点) p ( p1, p2, p3) ,摄像机的正上方指向 u ( u1, u2, u3) (上图摄像机的绿色箭头),摄影机朝向的反方向 l ( l1, l2, l3 )(上图摄像机的蓝色箭头),以及摄像机的右侧方向 r ( r1, r2, r3 )(上图摄像机的红色箭头) ,r 可以通过向量的叉乘得到
在这里插入图片描述
我们先将原来摄像机的坐标原点变换到世界坐标原点,并看向-z轴,然后将剩下的坐标轴旋转为世界坐标系新的三个坐标轴,也就是先做平移变换,再做旋转变换

在这里插入图片描述
其中 T 是平移矩阵, R是旋转矩阵

将摄像机原点变到世界坐标原点很简单,相机位置p ( p1, p2, p3 ) 变到(0, 0, 0)的矩阵为
在这里插入图片描述
移动到原点以后剩下的就是旋转矩阵了。正向去旋转矩阵比较麻烦,这里采用逆变换的思路,将世界坐标系三个轴 (1, 0, 0) , (0, 1, 0) , (0, 0, 1) 旋转到摄像机的三个坐标轴 r, u, l 是比较好求的,就是由r, u , l 三个单位的分量组成的
在这里插入图片描述
因此 R 就是上述变换矩阵的逆矩阵,又因为旋转矩阵是正交矩阵,所以旋转矩阵的逆矩阵等于它的转置

在这里插入图片描述

因此将摄像机中的点转换到世界坐标系中点的变化矩阵为
在这里插入图片描述

视图变换矩阵就是上述变换矩阵的逆矩阵,因为平移矩阵和旋转矩阵都是正交的,上述变换的逆矩阵就等于它的转置
在这里插入图片描述

glm库里的lookAt函数

搞清楚了视图变换矩阵的推导过程,我们来看看glm库里的视图变换,也就是常用的lookAt函数

glm::lookAt(eye, center, up);

lookAt函数有三个参数,分别代表观察点的位置,要看向的中心点,以及摄像机的up向量

GLM_FUNC_QUALIFIER mat<4, 4, T, Q> lookAt(vec<3, T, Q> const& eye, vec<3, T, Q> const& center, vec<3, T, Q> const& up)
	{
		if(GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_LH_BIT)
			return lookAtLH(eye, center, up);
		else
			return lookAtRH(eye, center, up);
	}

这里其实会先判断是左手坐标系,和右手坐标系,因为左手坐标系和右手坐标系z轴的指向不同,因而最终的运算结果也有差异,OpenGL是右手坐标系,因此我们来看看lookAtRH函数

GLM_FUNC_QUALIFIER mat<4, 4, T, Q> lookAtRH(vec<3, T, Q> const& eye, vec<3, T, Q> const& center, vec<3, T, Q> const& up)
	{
		vec<3, T, Q> const f(normalize(center - eye));
		vec<3, T, Q> const s(normalize(cross(f, up)));
		vec<3, T, Q> const u(cross(s, f));

		mat<4, 4, T, Q> Result(1);
		Result[0][0] = s.x;
		Result[1][0] = s.y;
		Result[2][0] = s.z;
		Result[0][1] = u.x;
		Result[1][1] = u.y;
		Result[2][1] = u.z;
		Result[0][2] =-f.x;
		Result[1][2] =-f.y;
		Result[2][2] =-f.z;
		Result[3][0] =-dot(s, eye);
		Result[3][1] =-dot(u, eye);
		Result[3][2] = dot(f, eye);
		return Result;
	}

用center中心点减去eye观察者的位置点就可以得到一个由eye指向center的向量 f (这里都做了归一化),也就是上面推导过程的 -l ( l 指向了摄像机观察方向的反方向),接下来 f 与 up 向量做一个叉乘(cross)得到了 s ,也就是我们上面推导过程的 r 向量,然后 f 和 s 叉乘重新计算出归一化以后的新up向量 u ,我们看到最终的视图变化矩阵和我们上面推导出来的结果其实上是一样的
在这里插入图片描述
对比一下上面推导的结果, r 就是glm里的 s ,代表摄像机右侧方向的向量,l 就是glm里的 -f ,代表摄像机朝向的反方向,p 就是glm里的 eye , 代表摄像机位置
在这里插入图片描述
是不是完全一样呢?

好了,本期的推导就到这里,后面会分享投影矩阵的推导过程以及相应的代码例子

也欢迎关注个人公众号,移动开发,音视频,游戏引擎,图形图像,大厂内推,不定时更新

在这里插入图片描述

  • 9
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值