当前位置: 首页 > 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.lryc.cn/news/618110.html

相关文章:

  • 复杂提示词配置文件
  • Tricentis Tosca:现代软件测试的自动化利器
  • 内存作假常见方案可行性分析
  • MySQL,Redis重点面试题
  • 最短路问题从入门到负权最短路
  • 基于51单片机指纹识别管理门禁密码锁系统设计
  • 集成电路学习:什么是URDF Parser统一机器人描述格式解析器
  • 19.Linux DHCP服务
  • 数据结构:串、数组与广义表
  • 【Leetcode】随笔
  • 每日算法刷题Day61:8.11:leetcode 堆11道题,用时2h30min
  • 普通大学本科生如何入门强化学习?
  • 【ros-humble】4.C++写法巡场海龟(服务通讯)
  • Linux运维学习第十四周
  • 【3D Gen 入坑(1)】Hunyuan3D-Paint 2.1 安装 `custom_rasterizer` 报错完整排查
  • PyTorch基础(使用Numpy实现机器学习)
  • Vue 中的 Class 与 Style 绑定详解2
  • ubuntu24.04设置登陆背景图片
  • Pytest项目_day12(yield、fixture的优先顺序)
  • Web安全自动化测试实战指南:Python与Selenium在验证码处理中的应用
  • 【openEuler构建测试环境或部署嵌入式系统】openEuler生态扩容新路径:内网穿透工具cpolar助力多场景落地
  • Linux-FTP服务器搭建
  • 多路转接 select
  • 【数据结构入门】二叉树(1)
  • IoT/实现和分析 NB-IoT+DTLS+PSK 接入华为云物联网平台IoTDA过程,总结避坑攻略
  • 智能合约执行引擎在Hyperchain中的作用
  • 快速搭建前端playwright工程
  • FinQ4Cn: 基于 MCP 协议的中国 A 股量化分析
  • Java -- 集合 --Collection接口和常用的方法
  • Python网络爬虫(一) - 爬取静态网页