【OpenGL学习笔记九】坐标系统

目录

如何实现从不同的视角观察物体的效果

物体坐标转换过程

世界坐标系

实现坐标转换的几个矩阵

模型矩阵

观察矩阵

投影矩阵


如何实现从不同的视角观察物体的效果

试想需要在一个空间中摆放多个不同的物体,或者同一个物体摆在多个不同的位置,并且能够在空间中从不同的视角去看物体,这个改如何实现呢?就像用鼠标拖着箱子,如何360度环绕的旋转,通过滑动齿轮放大,缩小,这些都是如何实现的?

这些看似复杂的效果,最终在代码层面就是把最开始的物体的坐标乘以几个矩阵,将其坐标进行变换即可,因为摆放物体到不同的位置,或者从不同的视角看物体,其实可以想想为视角不变,改变物体的坐标即可。

着色器代码也是非常简单的,如下

#version 330
layout(location = 0) in vec3 posVertex;
layout(location = 1) in vec2 inTexCord;

out vec2 TexCord;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
    gl_Position = projection * view * model * vec4(posVertex, 1.0f);
    TexCord = inTexCord;
}

最主要的就是gl_Position = projection * view * model * vec4(posVertex, 1.0f);

可以看到就是矩阵相乘

vec4(posVertex, 1.0f)为物体的坐标矩阵

model,view,projection都为不同的矩阵

所以,只要改变model,view,projection这三个矩阵,就可以改变物体的坐标,从而实现从不同的视角观察物体的效果。

物体坐标转换过程

如上图所示,物体的坐标系统需要经过,局部坐标空间->世界空间坐标空间->观察者坐标空间->裁剪坐标空间->屏幕坐标空间的转换。

将物体的坐标变换到过度坐标系优点在于一些操作或者运算更加方便容易。比如对物体的修改在局部空间说的通,对物体做出相对其它物体的操作,在世界空间才说的通。

局部坐标是物体的起始坐标,可以看作为一个具体的立方体

世界空间坐标,可以相像为把立方体放到了某个空间中

观察者坐标,从摄像机或者某个观察者的角度,观察上面的空间

裁剪坐标会被处理到−1.0到1.0范围内,并判断哪些点将会出现在屏幕上。

最后,我们将裁剪坐标变换为屏幕坐标,我们将使用一个叫做视口变换(Viewport Transform)的过程。视口变换将位于−1.0到1.0范围的坐标变换到由glViewport函数所定义的坐标范围内。最后变换出来的坐标将会送到光栅器,将其转化为片段。

 

世界坐标系

世界坐标系始终是固定不变的。OpenGL使用右手坐标,这里有一个形象的方法:使用右手定则
X 是你的拇指
Y 是你的食指
Z 是你的中指
如果你把你的拇指指向右边,食指指向天空,那么中指将指向你的背后。我们的观察方向是Z轴负半轴的方向。

进行旋转操作时需要指定的角度θ的方向则由右手法则来决定,即右手握拳,大拇指直向某个坐标轴的正方向,那么其余四指指向的方向即为该坐标轴上的θ角的正方向(即θ角增加的方向),图中用圆弧形箭头标出:

实现坐标转换的几个矩阵

从上面的流程可以看出最终显示到显示器上面的screen space是local space经过以下三个矩阵转换之后得到的

model 矩阵,将局部坐标转换为世界空间坐标

view矩阵 ,将世界坐标转换为观察者坐标

projection矩阵,将观察者坐标,转为剪裁坐标

我们需要实现的就是给出这几个矩阵。

模型矩阵

模型矩阵可以实现对象的平移,缩放,旋转等变换。

如果我们将我们所有的物体导入到程序当中,它们有可能会全挤在世界的原点(0, 0, 0)上,这并不是我们想要的结果。我们想为每一个物体定义一个位置,从而能在更大的世界当中放置它们。世界空间中的坐标正如其名:是指顶点相对于(游戏)世界的坐标。如果你希望将物体分散在世界上摆放(特别是非常真实的那样),这就是你希望物体变换到的空间。物体的坐标将会从局部变换到世界空间;该变换是由模型矩阵(Model Matrix)实现的。

基于QT的代码实现

QMatrix4x4 model;        
model.setToIdentity();
model.translate(2.0f, 5.0f, -15.0f);
model.scale(0.8,0.8,0.8);
model.rotate(model_angle_, model_x_, model_y_, model_z_);

以上代码就创建了一个可以对物体进行平移,缩放和旋转的模型矩阵。

观察矩阵

通过一系列的位移和旋转的组合,平移旋转场景,从而使得特定的对象变换到摄像机前方,这些组合在一起的变换存储在观察矩阵中。

这里看着复杂,好在强大的矩阵变换,只要我们能够定义出摄像机的坐标就能够得到观察矩阵,任意一个坐标,只要跟观察矩阵相乘即可转换到观察矩阵空间。

所以只需要给出摄像机的坐标即可得到观察矩阵,世界坐标系中的坐标矩阵和观察矩阵相乘,即可将世界坐标转换到摄像机坐标系中。

如果你使用3个相互垂直(或非线性)的轴定义了一个坐标空间,你可以用这3个轴外加一个平移向量来创建一个矩阵,并且你可以用这个矩阵乘以任何向量来将其变换到那个坐标空间。这正是LookAt矩阵所做的,现在我们有了3个相互垂直的轴和一个定义摄像机空间的位置坐标,我们可以创建我们自己的LookAt矩阵了:

 其中R是右向量,U是上向量,D是方向向量P是摄像机位置向量。

把这个LookAt矩阵作为观察矩阵可以很高效地把所有世界坐标变换到刚刚定义的观察空间。

原理见摄像机 - LearnOpenGL CN

基于QT的代码实现

QMatrix4x4 view;
view.lookAt(QVector3D(0.0f, 0.0f, 0.0f),
            QVector3D(-1.0f, 0.0f, 0.0f),
            QVector3D(0.0f, 1.0f, 0.0f));

函数需要一个摄像机位置、目标位置和上向量,就能创建出一个观察矩阵。

摄像机位置,获取摄像机位置很简单。摄像机位置简单来说就是世界空间中一个指向摄像机位置的向量

目标位置,目标位置和摄像机位置一样,就是世界空间中一个指向目标的向量

上向量,摄像机向上的方向即头顶朝向的方向在世界坐标中的方向,

上向量(0.0, 1.0, 0.0),y轴为1,其余为0,表示头顶朝上

上向量(0.0,-1.0,0.0),即y轴为-1,其余为0。这样表示脑袋向下,即人眼倒着看

上向量(1.0,0.0,0.0);x轴为1,其余为0.即人的脑袋像右歪90度来看,即顺时针转90度(换个角度思考就是壶逆时针转90度)

投影矩阵

OpenGL期望所有的坐标顶点都要在一个特定的范围内,不在这个范围内的将被剪裁掉。

投影矩阵用于定义这个坐标范围。

投影矩阵有正射投影矩阵和投射投影矩阵

投影矩阵有正射投影矩阵和透射投影矩阵,透射投影矩阵能模拟出生活中理你越远的东西,看起来越小的场景。

正射投影

透射投影

投射投影矩阵不仅把给定的平截头体范围映射到剪裁空间,还会修改每个顶点的W值,离观察者越远的顶点的w值越大,顶点的坐标的每个分量都会除以它的w值,距离观察者越远,顶点坐标就越小。

基于QT实现的投射矩阵

QMatrix4x4 projection;
projection.perspective(45,(float)width()/height(),0.1,100);

如上代码,非常简单的就实现了一个投射矩阵

projection.perspective所做的其实就是创建了一个定义了可视空间的大平截头体,任何在这个平截头体以外的东西最后都不会出现在裁剪空间体积内,并且将会受到裁剪。一个透视平截头体可以被看作一个不均匀形状的箱子,在这个箱子内部的每个坐标都会被映射到裁剪空间上的一个点。下面是一张透视平截头体的图片:

它的第一个参数定义了fov的值,它表示的是视野(Field of View),并且设置了观察空间的大小。如果想要一个真实的观察效果,它的值通常设置为45.0f,但想要一个末日风格的结果你可以将其设置一个更大的值。第二个参数设置了宽高比,由视口的宽除以高所得。第三和第四个参数设置了平截头体的平面。我们通常设置近距离为0.1f,而远距离设为100.0f。所有在近平面和远平面内且处于平截头体内的顶点都会被渲染。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值