OpenGL学习总结(二)

来源:互联网 发布:淘宝开网店的流程 编辑:程序博客网 时间:2024/05/24 05:19

OpenGL的矩阵变换与坐标系统

一、矩阵变换
在OpenGL的世界当中,我们可以用向量表示一个顶点的位置,那么当我们要改变顶点位置的时候,我们应该怎么去处理呢?答案是使用矩阵乘向量的方式进行改变,常见的改变一共有三种——缩放,平移和旋转,所以对应的矩阵也有三种,不过在介绍这些矩阵之前我们先要了解什么事齐次坐标。

1、齐次坐标
简单的来讲,齐次坐标就是普通坐标(x,y,z)后面再多加w一个分量,变成(x1,y1,z1,w),而x1、y1、z1的值则是x/w、y/w、z/w的结果,使用这种坐标的好处是解决一些欧氏几何无法解决的问题,比如两条平行线在透视空间中相交于某一点这种问题。
普通的欧氏几何会有如下方程组:
A1X+B1Y+D1=0
A1X+B1Y+D2=0
很明显上述方程组并没有解
但是用了齐次坐标后可以变成如下坐标:
A1X+B1Y+D1W=0
A1X+B1Y+D2W=0
这样的话就能够得到一组(x,y,0)的解,来表示两条平行线相交于无穷远。

而对于OpenGL来说,有了齐次坐标可以使向量在3D空间中平移,还可以创造出3D效果。

2、缩放矩阵
这里写图片描述

3、平移矩阵
这里写图片描述

注意:若不使用齐次坐标的话,我们的向量根本无法完成平移!

4、旋转矩阵
这里写图片描述
沿任意轴旋转
这里写图片描述
显然旋转可以说是最为麻烦的空间变换,然而旋转的处理方式并不仅仅只有这一种,四元数可以更加简洁方便的解决旋转问题。详见:https://www.zhihu.com/question/23005815

5、使用GLM库进行计算
构建平移矩阵:

glm::mat4 trans;trans = glm::translate(trans, glm::vec3(1.0f, 1.0f, 0.0f));

构建缩放矩阵:

glm::mat4 trans;trans = glm::rotate(trans, 90.0f, glm::vec3(0.0, 0.0, 1.0));

构建旋转矩阵:

glm::mat4 trans;trans = glm::scale(trans, glm::vec3(0.5, 0.5, 0.5));  

若同时存在平移、缩放、旋转的时候,则一定要按如下顺序构建变换矩阵

glm::mat4 trans;trans = glm::translate(trans, glm::vec3(1.0f, 1.0f, 0.0f));trans = glm::rotate(trans, 90.0f, glm::vec3(0.0, 0.0, 1.0));trans = glm::scale(trans, glm::vec3(0.5, 0.5, 0.5));  

即先平移后旋转再缩放的顺序。
最后在vertexshader当中:

#version 330 corelayout (location = 0) in vec3 position;layout (location = 1) in vec3 color;layout (location = 2) in vec2 texCoord;out vec3 ourColor;out vec2 TexCoord;uniform mat4 transform;//用于接受变换矩阵void main(){    gl_Position = transform * vec4(position, 1.0f);    //一定要按照这个顺序写!因为矩阵乘法没有交换律!!!    ourColor = color;    TexCoord = vec2(texCoord.x, 1.0 - texCoord.y);}

二、坐标系统

1、五大空间

局部空间(Local Space,或者称为物体空间(Object Space))世界空间(World Space)观察空间(View Space,或者称为视觉空间(Eye Space))裁剪空间(Clip Space)屏幕空间(Screen Space)

局部空间是指物体所在的坐标空间,即对象最开始所在的地方。想象你在一个建模软件(比如说Blender)中创建了一个立方体。你创建的立方体的原点有可能位于(0, 0, 0),即便它有可能最后在程序中处于完全不同的位置。甚至有可能你创建的所有模型都以(0, 0, 0)为初始位置(译注:然而它们会最终出现在世界的不同位置)。所以,你的模型的所有顶点都是在局部空间中:它们相对于你的物体来说都是局部的。

如果将我们所有的物体导入到程序当中,它们有可能会全挤在世界的原点(0, 0, 0)上,这并不是我们想要的结果。我们想为每一个物体定义一个位置,从而能在更大的世界当中放置它们。世界空间中的坐标正如其名:是指顶点相对于(游戏)世界的坐标。如果你希望将物体分散在世界上摆放(特别是非常真实的那样),这就是你希望物体变换到的空间。物体的坐标将会从局部变换到世界空间;该变换是由模型矩阵(Model Matrix)实现的。
模型矩阵是一种变换矩阵,它能通过对物体进行位移、缩放、旋转来将它置于它本应该在的位置或朝向。你可以将它想像为变换一个房子,你需要先将它缩小(它在局部空间中太大了),并将其位移至郊区的一个小镇,然后在y轴上往左旋转一点以搭配附近的房子。你也可以把上一节将箱子到处摆放在场景中用的那个矩阵大致看作一个模型矩阵;我们将箱子的局部坐标变换到场景/世界中的不同位置。

观察空间经常被人们称之OpenGL的摄像机(Camera)(所以有时也称为摄像机空间(Camera Space)或视觉空间(Eye Space))。观察空间是将世界空间坐标转化为用户视野前方的坐标而产生的结果。因此观察空间就是从摄像机的视角所观察到的空间。而这通常是由一系列的位移和旋转的组合来完成,平移/旋转场景从而使得特定的对象被变换到摄像机的前方。这些组合在一起的变换通常存储在一个观察矩阵(View Matrix)里,它被用来将世界坐标变换到观察空间。

在一个顶点着色器运行的最后,OpenGL期望所有的坐标都能落在一个特定的范围内,且任何在这个范围之外的点都应该被裁剪掉(Clipped)。被裁剪掉的坐标就会被忽略,所以剩下的坐标就将变为屏幕上可见的片段。这也就是裁剪空间(Clip Space)名字的由来。
因为将所有可见的坐标都指定在-1.0到1.0的范围内不是很直观,所以我们会指定自己的坐标集(Coordinate Set)并将它变换回标准化设备坐标系,就像OpenGL期望的那样。
为了将顶点坐标从观察变换到裁剪空间,我们需要定义一个投影矩阵(Projection Matrix),它指定了一个范围的坐标,比如在每个维度上的-1000到1000。投影矩阵接着会将在这个指定的范围内的坐标变换为标准化设备坐标的范围(-1.0, 1.0)。所有在范围外的坐标不会被映射到在-1.0到1.0的范围之间,所以会被裁剪掉。在上面这个投影矩阵所指定的范围内,坐标(1250, 500, 750)将是不可见的,这是由于它的x坐标超出了范围,它被转化为一个大于1.0的标准化设备坐标,所以被裁剪掉了。

2、如何呈现出最后的场景
这里写图片描述

首先我们要通过模型矩阵将局部空间坐标系转变成世界空间坐标系,其次通过观察矩阵将世界空间坐标系转变成观察空间坐标系,然后通过投影矩阵将观察空间坐标系变成裁剪空间坐标系。完成上述三个步骤,即可得到所需的视觉场景了。

3、模型矩阵
模型矩阵本质上就是一个变换矩阵,我们只需要完成平移、旋转、缩放这些工作即可。

glm::mat4 model;model = glm::translate(model, cubePositions[i]);model = glm::rotate(model, 45.0f, glm::vec3(1.0f, 0.3f, 0.5f));model = glm::scale(model, glm::vec3(0.5, 0.5, 0.5));  

4、观察矩阵
这个矩阵主要是用于摄像机观察世界空间所需要的,我们在这里先简单的的定义一下

glm::mat4 view;view = glm::translate(view, glm::vec3(0.0f, 0.0f, -3.0f));//其实就是将整个世界向z轴正方向平移了3个单位

5、投影矩阵
投影分为正射投影和透视投影。
正射投影:
这里写图片描述

glm::ortho(0.0f, 800.0f, 0.0f, 600.0f, 0.1f, 100.0f);//前两个参数指定了宽度,三四指定了高度,五六指定了近平面和远平面举例摄像机的距离

透视投影:
这里写图片描述

glm::mat4 proj = glm::perspective(45.0f, (float)width/(float)height, 0.1f, 100.0f);//它的第一个参数定义了fov的值,它表示的是视野(Field of View),并且设置了观察空间的大小。如果想要一个真实的观察效果,它的值通常设置为45.0f。第二个参数设置了宽高比,由视口的宽除以高所得。第三和第四个参数设置了平截头体的近和远平面。我们通常设置近距离为0.1f,而远距离设为100.0f。所有在近平面和远平面内且处于平截头体内的顶点都会被渲染。

PS:透视矩阵推导详见:http://blog.sina.com.cn/s/blog_753d53cd0101qnv4.html

六、在Shader中组合到一起

#version 330 corelayout (location = 0) in vec3 position;layout (location = 2) in vec2 texCoord;out vec2 TexCoord;uniform mat4 model;uniform mat4 view;uniform mat4 projection;void main(){    gl_Position = projection * view * model * vec4(position, 1.0f);    TexCoord = vec2(texCoord.x, 1.0 - texCoord.y);}
0 0
原创粉丝点击