OpenGL基础(12)图形变化与3D效果

1 向量、矩阵与图形变化的关系

在研究图形动态变化 就要用到数学方面的知识,涉及向量、矩阵以及相关的混合运算。关于向量、矩阵的基础知识可以参考视频:可汗学院线性代数课程(免费)

图形的缩放、旋转、平移基本操作以及 它们的组合操作 均是通过矩阵运算来实现。OpenGL没有自带任何的矩阵和向量知识,所以我们必须定义自己的数学类和函数。目前有一个易于使用,专门为OpenGL量身定做的数学库,那就是GLM。接下来专门使用GLM来实现各种向量、矩阵的操作。

2 GLM库基本操作

GLM(OpenGL Mathematics)是一个只有头文件的库,只需包含对应的头文件就可以了。GLM可以在它们的GLM网站上下载。把头文件的根目录复制到includes 文件夹,就可以使用这个库了。我们需要的GLM的大多数功能都可以从下面这3个头文件中找到:

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

2.1 GLM基本练习

@1 平移操作:

//创建向量(1,0,0)
glm::vec4 vec(1.0f, 0.0f, 0.0f, 1.0f);
//创建矩阵,默认是一个4×4的单位矩阵
glm::mat4 trans;
//单位矩阵平移向量(1,1,0)
trans = glm::translate(trans, glm::vec3(1.0f, 1.0f, 0.0f));
//把一个向量(1, 0, 0)位移(1, 1, 0)个单位
vec = trans * vec;
//输出(2,1,0)
std::cout << vec.x << vec.y << vec.z << std::endl;

@2 旋转+缩放 组合操作

把物品逆时针旋转90度。然后缩放0.5倍,使它变成原来的一半大。我们先来创建变换矩阵,如下所示:

//创建4*4的单位矩阵
glm::mat4 trans;
//新矩阵构建:沿z轴旋转90度
trans = glm::rotate(trans, 90.0f, glm::vec3(0.0, 0.0, 1.0));
//新矩阵构建:每个轴都缩放到0.5倍,然后沿z轴旋转90度
trans = glm::scale(trans, glm::vec3(0.5, 0.5, 0.5));  

@3 随时间变化的矩阵

//创建4*4的单位矩阵
glm::mat4 trans;
//创建新矩阵:平移矩阵(0.5,-0.5,0)
trans = glm::translate(trans, glm::vec3(0.5f, -0.5f, 0.0f));
//创建新矩阵:沿着z轴旋转(旋转矩阵随时间变化 持续变化角度),平移矩阵(0.5,-0.5,0)
trans = glm::rotate(trans,(GLfloat)glfwGetTime() * 50.0f, glm::vec3(0.0f, 0.0f, 1.0f));

2.2 矩阵传递给着色器

如果想把矩阵传递给顶点着色器,我们将使用uniform机制 修改顶点着色器让其接收一个mat4的uniform变量,然后再用矩阵uniform乘以位置向量,如下所示:

#version 330 core
layout (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);
}

这样就可以通过矩阵 实现 平移、旋转、缩放 以及他们组合在一起的功能了。在程序代码中,需要添加以下内容以便于 和着色器之间传递 矩阵信息,代码如下所示:

//查询uniform变量的地址
GLuint transformLoc = glGetUniformLocation(ourShader.Program, "transform");
/*目的:把矩阵数据发送给着色器
  第一个参数:uniform的位置值(location)。 
  第二个参数:告诉OpenGL我们将要发送多少个矩阵,这里是1。
  第三个参数:询问我们我们是否希望对我们的矩阵进行置换(Transpose),即交换我们矩阵的行和列。
  说明:OpenGL开发者通常使用内部矩阵布局,叫做列主序(Column-major Ordering)布局。GLM的默认布局是列主序,所以不需要置换矩阵,填GL_FALSE。
  第四个参数:真正的矩阵数据,但是GLM并不是把它们的矩阵储存为OpenGL所希望接受的那种,因此用GLM的自带的函数value_ptr来变换这些数据。
 */
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(trans));

3 GLM基于坐标系统的矩阵操作

3.1 投影(正射投影与投射投影)

@1 正射投影

正射投影(Orthographic Projection)矩阵定义了一个类似立方体的平截头体,指定了一个裁剪空间,每一个在这空间外面的顶点都会被裁剪。GLM创建正射投影的函数为:

/*目的:创建一个正射投影矩阵
 第一/二个参数:指定了平截头体的左右坐标。
 第三/四个参数:指定了平截头体的底部和上部。定义了近平面和远平面的大小,
 第五/六个参数:定义了近平面和远平面的距离。
*/
glm::ortho(0.0f, 800.0f, 0.0f, 600.0f, 0.1f, 100.0f);

射投影矩阵直接将坐标映射到屏幕的二维平面内,但实际上一个直接的投影矩阵将会产生不真实的结果,因为这个投影没有将透视(Perspective)考虑进去。所以我们需要透视投影矩阵来解决这个问题。

@2 投射投影

投射投影效果如下:

 简而言之,越远的东西看起来更小,这个效果我们称之为透视。在GLM中可以这样创建一个透视投影矩阵:

/*目的:创建一个透视投影矩阵
 第一个参数:定义了fov(Field of View)的值,设置观察空间的大小。对于一个真实的观察效果设置为45.0。
 第二个参数:设置了宽高比,由视口的高除以宽。 
 第三/四个参数:设置了平截头体的近和远平面。我们经常设置近距离为0.1而远距离设为100.0。所有在近平面和远平面的顶点且处于平截头体内的顶点都会被渲染。
*/
glm::mat4 proj = glm::perspective(45.0f, (float)width/(float)height, 0.1f, 100.0f);

3.2 组合矩阵(基于坐标系统)

我们为上述的每一个步骤都创建了一个转换矩阵:模型矩阵、观察矩阵和投影矩阵。一个顶点的坐标将会根据以下过程被转换到裁剪坐标:

我们创建了转换矩阵,接下来也是将它们传入着色器。传递方法同上@2.2部分,做一些变化,如下所示:

#version 330 core
layout (location = 0) in vec3 position;
...
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
    // 注意从右向左读
    gl_Position = projection * view * model * vec4(position, 1.0f);
    ...
}

4 Z缓冲区

通过前面这些基本的操作,我们了解到2D平面物品在3D空间中的变化。但如果涉及 正方体 这种东西在空间展示时,就需要深度测试了,而OpenGL存储所有深度信息于Z缓冲区(Z-buffer)中,也被称为深度缓冲区(Depth Buffer)

深度测试(Depth Testing)GLFW会自动生成这样一个缓冲区。深度存储在每个片段里面(作为片段的z值)当片段像输出它的颜色时,OpenGL会将它的深度值和z缓冲进行比较然后如果当前的片段在其它片段之后它将会被丢弃,然后重写。

我们通过glEnable函数来开启深度测试(OpenGL默认关闭)。glEnable和glDisable函数允许我们开启或关闭某一个OpenGL的功能。该功能会一直是开启或关闭的状态直到另一个调用来关闭或开启它。现在我们想开启深度测试就需要开启GL_DEPTH_TEST:

glEnable(GL_DEPTH_TEST);

既然我们使用了深度测试我们也想要在每次重复渲染之前清除深度缓冲区(否则前一个片段的深度信息仍然保存在缓冲区中)。就像清除颜色缓冲区一样,我们可以通过在glclear函数中指定DEPTH_BUFFER_BIT位来清除深度缓冲区:

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

该系列文章主要参考openGL官网 和学习 learnopenGL官网 进行知识体系的梳理和重构,重在形成自己对openGL知识的理解和知识体系。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

图王大胜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值