1.5 变换
为了使物体变成动态的,需要使用多个矩阵对象变换一个物体。
1.5.1 向量
向量包含方向和模。由于向量是一个方向,很难用位置表示,因此可指定原点,然后指向一个方向,对应一个点,使其变为位置向量。
1、向量与标量运算:数学中是没有这个运算的,但是在许多线性代数的库中支持这样的运算。该运算就是将向量的每个元素,与标量进行运算。其中的+可以是+,-,·或÷。注意-和÷运算时不能颠倒(标量-/÷向量),因为颠倒的运算是没有定义的。

2、向量取反:向量前面加上负号,使得方向逆转。
3、向量加减:对应分量相加减,其几何意义为:


4、向量点乘,两个向量点乘是对应分量相乘再相加,结果是一个标量,同时也等于两个两个向量的模相乘再乘以余弦值,通过下面两个式子,可以通过点乘计算两个向量的夹角,这可用于光照的计算:


5、向量叉乘:两个不平行向量的叉乘结果,为一个正交于两个输入向量的向量

6、在GLSL中,还定义了两个向量的逐元素乘法(componet wise),vec4 c=a*b,结果仍为一个向量。
1.5.2 矩阵
1、矩阵与标量的加减,就是每个分量与标量做运算,同样在数学中是不存在的。
2、矩阵与标量数乘,每个分量相乘。标量用它的值缩放(Scale)矩阵的所有元素。
3、矩阵与向量相乘:向量变为齐次坐标,增加w分量,w分量一般为1,主要用于向量位移。
- 缩放,对向量长度进行更改

- 位移

- 使用齐次坐标可以允许我们在3D向量上进行位移。如果一个向量w的齐次坐标是0,这个坐标就是方向向量(Direction Vector),这个向量就不能位移(不能位移一个方向)。
- 旋转,沿x轴旋转

1.5.3 实践
1、下载GLM库进行矩阵运算,链接,该库只有头文件,需在vs包含目录里添加对应的头文件文件夹,在项目里包含头文件:
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
2、定义矩阵
- 0.9.9版本以前的GLM,默认会将矩阵类型初始化为单位矩阵。
- 按顺序定义位移、旋转和缩放矩阵,GLM会自动将矩阵相乘,返回的结果是一个包括了多个变换的变换矩阵,即
trans=TranslateMat*RotateMat*ScaleMat。 - 对上述矩阵的理解顺序,如果绕着固定坐标系变换,则上述变换为左乘,先缩放,再绕正坐标系Z轴旋转,再平移。
- 即实际的变换顺序应该与阅读顺序相反:尽管在代码中我们先位移再旋转,实际的变换却是先应用旋转再是位移的。
- 平移矩阵中,定义三维方向向量
glm::vec3(1.0f, 1.0f, 0.0f)表示平移,注意这里不是齐次矩阵,只有定义顶点数据时,才利用齐次矩阵。 - 在旋转矩阵中,利用
glm::radians(0.1f)将度数转为弧度。
glm::mat4 trans;//单位矩阵
trans = glm::translate(trans, glm::vec3(0, 0, 1.0));
trans = glm::rotate(trans, glm::radians(45.0f), glm::vec3(0, 0, 1.0f));
trans = glm::scale(trans, glm::vec3(0.5, 0.5, 0.5));
3、如何把矩阵传递给着色器?在GLSL里也有一个mat4类型,所以修改顶点着色器让其接收一个mat4的uniform变量,然后再用矩阵uniform乘以位置向量:
#version 330 core
layout(location = 0) in vec3 aPos;
layout(location = 1) in vec3 aColor;
layout(location = 2) in vec2 aTexCoord;
out vec3 vertexColor;
out vec2 TexCoord;
uniform mat4 tansform;
void main()
{
gl_Position =tansform*vec4(aPos.x, aPos.y, aPos.z, 1.0);
vertexColor=aColor;
TexCoord=aTexCoord;
}
4、将矩阵传递给着色器。
- 用有
Matrix4fv后缀的glUniform函数把矩阵数据发送给着色器。第一个参数是uniform的位置值。第二个参数为矩阵数量。第三个参数为是否需要转置。最后一个参数是真正的矩阵数据,但是GLM并不是把它们的矩阵储存为OpenGL所希望接受的那种,因此我们要先用GLM的自带的函数value_ptr来变换这些数据。
unsigned int transformLoc = glGetUniformLocation(ourShader.ID, "transform");
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(trans));

1.6 坐标系统
1.6.1 概述
OpenGL在顶点着色器中会将范围内的坐标变换为标准化设备坐标(-1.0到1.0之间),然后传入光栅器(Rasterizer),变换为屏幕上的二维坐标或像素。
在上述过程中,顶点会经过几个变换矩阵被变换到多个坐标系统。顶点坐标起始于局部坐标,经模型矩阵后变为世界坐标,经观察矩阵后变成观察坐标,经投影矩阵后变成裁剪坐标,并最后以屏幕坐标的形式结束。

1、局部坐标:某个物体相对于自身局部坐标系原点的坐标。
2、世界坐标:全部物体摆放于世界空间中,某个物体相对于世界坐标系原点的坐标。利用模型矩阵,可以将物体模型放置到场景中的某个位置。
3、观察坐标:某个物体相对于摄像机坐标系原点的坐标。
4、裁剪坐标:为了将顶点坐标从观察变换到裁剪空间,我们需要定义一个投影矩阵(Projection Matrix),它指定了一个范围的坐标,比如在每个维度上的-1000到1000。投影矩阵会将范围内的坐标变换为标准化设备坐标。投影矩阵分为正射投影矩阵(Orthographic Projection Matrix)和透视投影矩阵(Perspective Projection Matrix),由投影矩阵创建的观察箱(Viewing Box)被称为平截头体(Frustum)。
5、屏幕坐标: 所有顶点被变换到裁剪空间后,会自动执行透视除法(Perspective Division),将位置向量的x,y,z分量分别除以向量的齐次w分量;透视除法是将4D裁剪空间坐标变换为3D标准化设备坐标的过程。这一步会在顶点着色器运行的最后自动执行。然后OpenGL会通过glViewport函数将坐标映射到屏幕坐标。
1.6.2 投影矩阵
1、 正射投影矩阵定义了一个类似立方体的平截头箱,创建一个正射投影矩阵需要指定宽、高、近(Near)平面和远(Far)平面。正射平截头体直接将坐标映射为标准化设备坐标,因为每个向量的w分量都没有进行改变,这也导致正射投影矩阵在屏幕上产生的物体没有远近之分,效果不真实。
//创建正射投影矩阵,分别对应左右坐标,底部顶部坐标,近平面和远平面
glm::ortho(0.0f, 800.0f, 0.0f, 600.0f, 0.1f, 100.0f);

2、透视投影矩阵:在真实世界中,离得越远的物体看起来更小,两条线在很远的地方会相交。为模仿这样的效果,使用透视投影矩阵。

-
透视投影矩阵将给定的平截头体范围映射到裁剪空间,并且修改顶点坐标的w值,使得离观察者越远的顶点坐标w分量越大。被变换到裁剪空间的坐标都会在-w到w的范围之间。这也就使得在透视除法时,顶点坐标的每个分量都除以它的w分量,距离观察者越远顶点坐标就会越小。
-
透视平截头体可以被看作一个不均匀形状的箱子。
//定义透视投影矩阵,第一个参数为视角大小,通常为45; //第二个参数为宽高比,由视口的宽除以高 //第三四参数为近远平面,一般为0.1与100 glm::mat4 proj = glm::perspective(glm::radians(45.0f)

本文详细介绍了OpenGL中的向量和矩阵运算,如缩放、位移、旋转,以及如何通过GLM库构建变换矩阵。重点讲解了投影矩阵的正射和透视,以及如何用观察矩阵和摄像机坐标系实现3D场景的动态变换。此外,文章涵盖了鼠标和键盘输入控制摄像机移动的方法。
最低0.47元/天 解锁文章
1754

被折叠的 条评论
为什么被折叠?



