当前位置: 首页 > news >正文

【OpenGL】LearnOpenGL学习笔记06 - 坐标系统、MVP变换、绘制立方体

上接:https://blog.csdn.net/weixin_44506615/article/details/150052880?spm=1001.2014.3001.5501
完整代码:https://gitee.com/Duo1J/learn-open-gl

一、坐标系统与MVP变换

首先我们需要了解几个坐标系统

1. 局部空间 (Local Space,或是称为物体空间 Object Space)
物体本身所在的坐标空间,例如我们在DCC软件(Max、Maya、Blender等)中创建了一个立方体,它默认在(0, 0, 0)的位置,此时基于的坐标系就是局部空间坐标系
2. 世界空间 (World Space)
在我们的场景中可能会有很多的物体,我们使用世界空间来描述这些物体在场景中的位置,通过 模型矩阵 (Model Matrix) 变换而来
3. 观察空间 (View Space)
我们观察3D场景是会在世界空间中的某一个位置,朝向某一个方向来观察,这个点就是我们摄像机所处的位置,那么基于我们这个观察点所建立的坐标系就是观察空间,通过 观察矩阵 (View Matrix) 变换而来
4. 裁剪空间 (Clip Space)
前文中提到,在顶点着色器最后,我们需要将坐标转化为标准设备坐标(NDC),这个坐标系三个轴的范围均在(-1, 1)之间,也就是说我们需要将顶点的坐标限制在这个范围内,超出的部分会被裁剪掉,这个坐标系就称为裁剪空间,通过 投影矩阵 (Projection Matrix) 变换而来
为此,我们需要一个观察箱,也被称作 平截头体(Frustum) 来表示这个范围,同时还分为正交和透视投影
5. 屏幕空间 (Screen Space)
屏幕空间即为最后输出为2D图像的坐标系,NDC坐标会基于当前分辨率转换为屏幕空间坐标

上面进行 局部 -> 世界 -> 观察 -> 裁剪 一系列变换的矩阵就统称为 MVP 变换矩阵 (Model View Projection Matrix)

二、立方体绘制

接下来我们动手应用MVP变换来绘制一个立方体,首先修改一下我们的顶点数据为立方体的顶点

float vertices[] = {-0.5f, -0.5f, -0.5f,  0.0f, 0.0f,0.5f, -0.5f, -0.5f,  1.0f, 0.0f,0.5f,  0.5f, -0.5f,  1.0f, 1.0f,0.5f,  0.5f, -0.5f,  1.0f, 1.0f,-0.5f,  0.5f, -0.5f,  0.0f, 1.0f,-0.5f, -0.5f, -0.5f,  0.0f, 0.0f,-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,0.5f, -0.5f,  0.5f,  1.0f, 0.0f,0.5f,  0.5f,  0.5f,  1.0f, 1.0f,0.5f,  0.5f,  0.5f,  1.0f, 1.0f,-0.5f,  0.5f,  0.5f,  0.0f, 1.0f,-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,-0.5f,  0.5f,  0.5f,  1.0f, 0.0f,-0.5f,  0.5f, -0.5f,  1.0f, 1.0f,-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,-0.5f,  0.5f,  0.5f,  1.0f, 0.0f,0.5f,  0.5f,  0.5f,  1.0f, 0.0f,0.5f,  0.5f, -0.5f,  1.0f, 1.0f,0.5f, -0.5f, -0.5f,  0.0f, 1.0f,0.5f, -0.5f, -0.5f,  0.0f, 1.0f,0.5f, -0.5f,  0.5f,  0.0f, 0.0f,0.5f,  0.5f,  0.5f,  1.0f, 0.0f,-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,0.5f, -0.5f, -0.5f,  1.0f, 1.0f,0.5f, -0.5f,  0.5f,  1.0f, 0.0f,0.5f, -0.5f,  0.5f,  1.0f, 0.0f,-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,-0.5f,  0.5f, -0.5f,  0.0f, 1.0f,0.5f,  0.5f, -0.5f,  1.0f, 1.0f,0.5f,  0.5f,  0.5f,  1.0f, 0.0f,0.5f,  0.5f,  0.5f,  1.0f, 0.0f,-0.5f,  0.5f,  0.5f,  0.0f, 0.0f,-0.5f,  0.5f, -0.5f,  0.0f, 1.0f
};

这次的顶点数据结构为顶点坐标 + UV坐标,所以我们需要修改一下顶点属性,去掉之前设置的顶点色

int step = 5, curStep = 0;
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, step * sizeof(float), (void*)curStep);
glEnableVertexAttribArray(0);
curStep += 3;
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, step * sizeof(float), (void*)(curStep * sizeof(float)));
glEnableVertexAttribArray(1);
curStep += 2;

接下来我们来计算并设置MVP矩阵

// 全局定义宽高
float screenWidth = 800;
float screenHeight = 600;// ...// 由于MVP矩阵易变,所以在主循环中计算
// Model矩阵,进行旋转
glm::mat4 model = glm::mat4(1);
model = glm::rotate(model, glm::radians(50.0f), glm::vec3(1, 1, 0));// View矩阵,相当于相机向后退3个单位
glm::mat4 view = glm::mat4(1);
view = glm::translate(view, glm::vec3(0, 0, -3));// Projection矩阵
glm::mat4 projection = glm::mat4(1);
// 参数1:FOV值
// 参数2:宽高比
// 参数3:近平面距离 Near Plane
// 参数4:远平面距离 Far Plane
projection = glm::perspective(glm::radians(45.0f), screenWidth / screenHeight, 0.1f, 100.0f);// 设置顶点着色器中的uniform变量
shader.SetMat4("model", glm::value_ptr(model));
shader.SetMat4("view", glm::value_ptr(view));
shader.SetMat4("projection", glm::value_ptr(projection));

其中,投影矩阵的相关参数可参考下图 (图片来自于LearnOpenGL)
在这里插入图片描述

修改顶点着色器

#version 330 core// 去掉顶点色
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord;out vec2 texCoord;// MVP矩阵
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;void main()
{// 注意顺序gl_Position = projection * view * model * vec4(aPos, 1.0);texCoord = aTexCoord;
}

片段着色器中去掉顶点色

#version 330 coreout vec4 FragColor;// 去掉顶点色
in vec2 texCoord;uniform sampler2D texture1;
uniform sampler2D texture2;void main()
{FragColor = mix(texture(texture1, texCoord), texture(texture2, texCoord), 0.2);
}

最后将绘制方式改为

glDrawArrays(GL_TRIANGLES, 0, 36);

编译运行,顺利的话可以看见下图
立方体绘制
我们还可以使用glfwGetTime()来让它旋转起来

model = glm::rotate(model, (float)glfwGetTime() * glm::radians(50.0f), glm::vec3(1, 1, 0));

我们可以发现,这个立方体好像有点奇怪,我们无法辨别出他的内外,这是由于离我们远的平面由于后绘制的原因会盖在靠前的平面上
接下来我们就需要使用到 Z缓冲(Z - Buffer) 也称 深度缓冲 (Depth Buffer) 来解决这个问题
当片段要输出它的颜色时,OpenGL会将它的深度与Z Buffer中的这个位置的深度值进行比较,如果该片段的深度在已写入的深度值之后的话,就会丢弃该片段,反之将覆盖Z Buffer中的深度值,这个过程称为 深度测试 (Depth Testing)
我们可以通过以下代码来开启深度测试

glEnable(GL_DEPTH_TEST);

同时在清理时,除了颜色缓冲,我们还需要加上深度缓冲

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

这样,我们就得到了一个正确的立方体
立方体绘制、深度缓冲
完整代码请见开头的git仓库

http://www.dtcms.com/a/326720.html

相关文章:

  • 用 t-SNE 把 KSC 高光谱“变成可转动的 3D 影像”——从零到会,逐段读懂代码并导出旋转 GIF
  • 二叉树进阶 之 【模拟实现二叉搜索树】(递归、非递归实现查找、插入、删除功能)
  • 跨平台RTMP推流SDK vs OBS:技术差异与行业落地解析
  • 01数据结构-十字链表和多重邻接表
  • Lwip深度阅读-网络架构
  • 【代码随想录day 17】 力扣 654.最大二叉树
  • 贪心----2.跳跃游戏
  • 区块链技术原理(5)-网络
  • Docker部署MySQL完整指南:从入门到实践
  • Leetcode-25.K个一组翻转链表
  • 【13-向量化-高效计算】
  • 第二十一天:统计数字
  • 嵌入式系统学习Day16(C语言中的位运算)
  • 绿巨人VS Code多开项目单独管理每个项目单独使用一个不限制的augment
  • 构建AI代理工作流的开源利器——Sim Studio
  • 文件编辑html
  • C语言命令行参数
  • 北京JAVA基础面试30天打卡07
  • 【C++竞赛】核桃CSP-J模拟赛题解
  • 提示词工程实战:用角色扮演让AI输出更专业、更精准的内容
  • vagrant和itamae怎么配合使用? (放弃)
  • 33Nginx模块的从配置与优化
  • 如何使用curl编程来下载文件
  • MacBook 本地化部署 Dify 指南
  • AIDL简单使用
  • 【接口自动化测试】---YAML、JSON Schema
  • 逐际动力开源运控 tron1-rl-isaacgym 解读与改进
  • VMD例程(Matlab 2021b可直接使用)
  • 从“目标烂尾”到“100%交付”:谷歌OKR追踪系统如何用“透明化+强问责”打造职场责任闭环
  • 小白入门指南:Edge SCDN 轻松上手