GL中的坐标系是标准设备坐标,即他的每个坐标轴的取值范围都是[-1.0,1.0]。通常,我们输入到顶点着色器中的顶点坐标都会被转换为标准化设备坐标,然后进行光栅化,转变成屏幕坐标。然而事实上,从顶点坐标到屏幕坐标是一个较为复杂的过程。总体来讲为了某些计算更加方便,会经过5个坐标系统的变换:

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

下面就是坐标系统的具体意义。

1. 概述

经过了这么久的介绍,我们都一直在绘制屏幕中的一个矩形。而事实上,之前的一切过程,我们还只是停留在建造模型的过程。这时候我们的画布里只有这一个模型,我们把当前的坐标系统称作是局部空间

当然,我们的屏幕中大多数情况下不可能只有一个模型,而是千千万万个模型。我们应该通过一系列矩阵变换将模型变换到一个更大的画布中。当然,我们的屏幕是不可能变大的,所以我们是通过一系列的矩阵缩放我们的模型然后放到原始画布中来模拟把模型放入大画布中这个过程。这里,我们把变换到大画布后的坐标系统称为是世界空间。而从局部空间变换到世界空间转换所需要的这个矩阵,我们成为模型矩阵

我们知道,OpenGL是一个3维的世界,然而我们屏幕是一个2维的画面。就好像我们生活在3维空间中但是我们所观察的世界实际是以我们的眼睛作为起始点获取的3维空间在我们的视网膜上投影在将信息传给我们的大脑。那么OpenGL模拟了这个过程,首先我们需要一个眼睛,再其次我们需要将世界投影到我们的屏幕上。

在OpenGL中我们把这个眼睛称作是摄像机。而摄像机针对的坐标系统称为观察空间。我们从世界空间转换到观察空间所经过的矩阵为观察矩阵。经过观察矩阵转换后,实际上我们看到的就是3维世界在我们摄像机所面对的方向上的一个投影了。

生活中我们有一个常识,物体*大远小。物体距离我们越远,他看起来将会越小,我们把这种现象称为透视现象。然而在观察空间我们看到的投影缺不具备这种特点,我们把这种按照物体原比例显示的投影称为正投影。然而这样的投影却与我们*常所观察到的世界不一样,为了让事物看起来更加真实,我们要给物体加上透视效果。经过透视的投影,就是透视投影。GL中,我们为了使物体具有透视效果,我们要将物体经过一个透视投影矩阵进行转换,转换至的空间我们称为裁剪空间。之所以称为裁剪空间,是因为除了投食之外,我们还要把超出视野的地方裁减掉。而GL中就是把超出屏幕空间的物体裁减掉。所以称之为裁剪空间。

最后,我们要把裁剪空间中的物体转换到我们的屏幕上进行输出。屏幕输出的空间我们叫做屏幕空间。这个过程呢,就不用我们费心了,因为到了裁剪空间之后我们已经完全完成了模型到透视投影的转换。接下来只需要将这部分物体展示在屏幕上就好,所以这部分工作由GL替我们完成。这个过程,我们叫视口变换。视口变换将位于-1.0到1.0范围的坐标变换到由glViewport函数所定义的坐标范围内。最后变换出来的坐标将会送到光栅器,将其转化为片段。

到这里我们已经大概清楚了这些空间的作用,那么在对应空间中的坐标就分别称为局部坐标(Local Coordinate)世界坐标(World Coordinate)观察坐标(View Coordinate)裁剪坐标(Clip Coordinate)屏幕坐标(Screen Coordinate)

2. 组合

我们知道,从我们的局部空间到屏幕空间需要我们先把局部坐标转换至裁剪坐标,再交由GL转换为屏幕坐标。所以我们应该经过的过程就是:

顺序一定不要搞错,记住矩阵乘法是从右向左的。

 3. 进入3D

这里我就不放全部代码了,先放一段模型构建的代码

 1 void configVAO(unsigned int * VAO,unsigned int * VBO,unsigned int * EBO) {
2 ///顶点数据
3 float vertices[] = {
4 0.5,0.5,0.5,0.0,0.0,0.0,
5 0.5,-0.5,0.5,1.0,0.0,0.0,
6 -0.5,-0.5,0.5,0.0,1.0,0.0,
7 -0.5,0.5,0.5,0.0,0.0,1.0,
8 0.5,0.5,-0.5,1,1,1,
9 0.5,-0.5,-0.5,0,1,1,
10 -0.5,-0.5,-0.5,1,0,1,
11 -0.5,0.5,-0.5,1,1,0
12 };
13
14 ///索引数据
15 unsigned int indices[] = {
16 0,1,2,
17 0,2,3,
18 1,4,5,
19 0,1,4,
20 5,6,7,
21 4,5,7,
22 2,3,6,
23 3,6,7,
24 0,3,4,
25 3,4,7,
26 1,5,6,
27 1,2,6,
28 };
29
30 ///创建顶点数组对象
31 glGenVertexArrays(1, VAO);
32
33 ///创建顶点缓冲对象
34 glGenBuffers(1, VBO);
35 ///创建索引缓冲对象
36 glGenBuffers(1, EBO);
37
38 ///绑定定点数组对象至上下文
39 glBindVertexArray(*VAO);
40
41 ///绑定定点缓冲对象至上下文
42 glBindBuffer(GL_ARRAY_BUFFER, *VBO);
43 ///把顶点数组复制到顶点缓冲对象中
44 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
45 ///设置顶点属性并激活属性
46 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
47 glEnableVertexAttribArray(0);
48 glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,6 * sizeof(float), (void*)(3 * sizeof(float)));
49 glEnableVertexAttribArray(1);
50 ///绑定索引缓冲对象至上下文
51 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, *EBO);
52 ///把索引数据复制到索引缓冲对象中
53 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
54
55 ///解除顶点数组对象的绑定
56 glBindVertexArray(0);
57 ///解除顶点缓冲对象的绑定
58 glBindBuffer(GL_ARRAY_BUFFER, 0);
59 ///解除索引缓冲对象的绑定
60 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
61 }

上面我们建立了一个正方体,八个顶点分别有八个颜色。目前,他还在局部空间内。

接下来我们来将他们转换到裁剪空间内:

 1 glm::vec3 postions[] = {
2 glm::vec3(0.0,0.0,0.0),
3 glm::vec3( 2.0f, 5.0f, -15.0f),
4 glm::vec3(-1.5f, -2.2f, -2.5f),
5 glm::vec3(-3.8f, -2.0f, -12.3f),
6 glm::vec3( 2.4f, -0.4f, -3.5f),
7 glm::vec3(-1.7f, 3.0f, -7.5f),
8 glm::vec3( 1.3f, -2.0f, -2.5f),
9 glm::vec3( 1.5f, 2.0f, -2.5f),
10 glm::vec3( 1.5f, 0.2f, -1.5f),
11 glm::vec3(-1.3f, 1.0f, -1.5f)
12 };
13
14 glm::mat4 view = glm::mat4(1.0f);
15 view = glm::translate(view, glm::vec3(0.f, 0.f, -3.f));
16 glm::mat4 projection = glm::mat4(1.0f);
17 projection = glm::perspective(glm::radians(45.0f), (float)(SCR_WIDTH * 1.0 / SCR_HEIGHT), 0.1f, 100.0f);
18 ourShader.setMtx4fv("view", view);
19 ourShader.setMtx4fv("projection", projection);
20
21 while (!glfwWindowShouldClose(window))
22 {
23 processInput(window);
24
25 ///设置清屏颜色
26 glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
27 ///清屏
28 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
29
30 ///绑定定点数组对象
31 glBindVertexArray(VAO);
32
33 for (int i = 0; i < 10; ++i) {
34 glm::mat4 model = glm::mat4(1.0f);
35 model = glm::translate(model, postions[i]);
36 float angle = 20.0f * i;
37 model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));
38 ourShader.setMtx4fv("model", model);
39
40 ///以索引绘制顶点数据
41 // glDrawArrays(GL_TRIANGLES, 0, 3);
42 glDrawElements(GL_TRIANGLES,36,GL_UNSIGNED_INT,0);
43 }
44
45
46 ///交换颜色缓冲
47 glfwSwapBuffers(window);
48 ///拉取用户事件
49 glfwPollEvents();
50 }

我们看到,我们为每个物体定单独定义了一个模型矩阵,这样,我们每个模型在世界空间中的状态都不同,然后在定义了唯一一个观察矩阵和透视投影矩阵,这样就模拟出我看眼睛看到物体的一个过程。

这里我们只对glm为我们提供的几个新出现的函数做一下简单讲解:

这是用来指定透视投影矩阵的函数。

  • 第一个参数radians指的是Fov,它表示的是视野(Field of View),并且设置了观察空间的大小。如果想要一个真实的观察效果,它的值通常设置为45.0f。他就是图中两个蓝色实线的空间夹角。

  • 第二个参数scale设置了宽高比,由视口的宽除以高所得。

  • 第三和第四个参数设置了*截头体的*和远*面。我们通常设置*距离为0.1f,而远距离设为100.0f。所有在**面和远*面内且处于*截头体内的顶点都会被渲染。图中粉色截面即为**面,蓝色截面即为远*面。

接下来虽然代码中没有,我们还是提一下正投影矩阵的创建方法:

当你把透视矩阵的 near 值设置太大时(如10.0f),OpenGL会将靠*摄像机的坐标(在0.0f和10.0f之间)都裁剪掉,这会导致一个你在游戏中很熟悉的视觉效果:在太过靠*一个物体的时候你的视线会直接穿过去。

OpenGL 坐标系统详解的更多相关文章

  1. Qt的Graphics-View框架和OpenGL结合详解

    Qt的Graphics-View框架和OpenGL结合详解 演示程序下载地址:这里 程序源代码下载地址:这里 这是一篇纯技术文,介绍了这一个月来我抽时间研究的成果. Qt中有一个非常炫的例子:Boxe ...

  2. 【OpenGL】详解第一个OpenGL程序

    写在前面 OpenGL能做的事情太多了!很多程序也看起来很复杂.很多人感觉OpenGL晦涩难懂,原因大多是被OpenGL里面各种语句搞得头大,一会gen一下,一会bind一下,一会又active一下. ...

  3. OpenGL ES 详解纹理生成和纹理映射步骤以及函数

    通常一个纹理映射的步骤是: 创建纹理对象.就是获得一个新的纹理句柄 ID. 指定纹理.就是将数据赋值给 ID 的纹理对象,在这一步,图像数据正式加载到了 ID 的纹理对象中. 设定过滤器.定义了ope ...

  4. OpenGL一些函数详解(二)

    OpenGL ES顶点数据绘制技巧 在OpenGL中,绘制一个长方体,需要将每个顶点的坐标放在一个数组中.保存坐标时有一些技巧(由于字母下标不好表示,因此将下标表示为单引号,如A1将在后文中表示为A' ...

  5. OpenGL ES一些函数详解(一)

    glLoadIdentity和glMultMatrix   glLoadIdentity的作用是将当前模型视图矩阵转换为单位矩阵(行数和列数相同的矩阵,并且矩阵的左上角至右下角的连线上的元素都为1,其 ...

  6. view坐标_ _ Android应用坐标系统全面详解

    转:http://blog.csdn.net/yanbober/article/details/50419117 1 背景 去年有很多人私信告诉我让说说自定义控件,其实通观网络上的很多博客都在讲各种自 ...

  7. OpenGL的glTranslatef平移变换函数详解

    OpenGL的glTranslatef平移变换函数详解 glTranslated()和glTranslatef()这两个函数是定义一个平移矩阵,该矩阵与当前矩阵相乘,使后续的图形进行平移变换. 我们先 ...

  8. OpenGL的glRotatef旋转变换函数详解

    OpenGL的glRotatef旋转变换函数详解 先看一下函数定义:void glRotatef(GLfloat angle,  GLfloat x,     GLfloat y,     GLflo ...

  9. [转]百度地图API详解之地图坐标系统

    博客原文地址:http://www.jiazhengblog.com/blog/2011/07/02/289/ 我们都知道地球是圆的,电脑显示器是平的,要想让位于球面的形状显示在平面的显示器上就必然需 ...

  10. OpenGL ES: (4) EGL API详解 (转)

    上一节我们初步学习了 OpenGL ES.EGL.GLSL 的相关概念,了解了它们的功能,以及它们之间的关联.我们知道了 EGL 是绘制 API(比如 OpenGL ES)与 底层平台窗口系统之间的接 ...

随机推荐

  1. [爬虫]2.2.1 使用Selenium库模拟浏览器操作

    Selenium是一个非常强大的工具,用于自动化Web浏览器的操作.它可以模拟真实用户的行为,如点击按钮,填写表单,滚动页面等.由于Selenium可以直接与浏览器交互,所以它可以处理那些需要Java ...

  2. ChatGPT 1.0.0安卓分析,仅限国内分享

    ChatGPT 1.0.0安卓分析,仅限国内分享 博客园首发,本文将对ChatGpt Android版本1.0.0 APK进行静态解包分析和抓包分析,从ChatGpt Android APK功能的设计 ...

  3. 借助 mkcert 和批处理命令生成局域网证书

    借助 mkcert 和批处理命令生成局域网证书 自动获取ipv4,一键生成很方便 cd /d %~dp0 ipconfig |find "IPv4" > ipv4 set / ...

  4. 关于 Task 简单梳理(C#)【并发编程系列】

    〇.前言 Task 是微软在 .Net 4.0 时代推出来的,也是微软极力推荐的一种多线程的处理方式. 在 Task 之前有一个高效多线程操作类 ThreadPool,虽然线程池相对于 Thread, ...

  5. uniapp开发H5,分享链接到微信好友,显示标题和缩略图

    本文档介绍了如何在UniApp开发中实现将链接分享到微信好友,并确保在分享时显示标题和缩略图的方法. 背景 第一次用uniapp开发H5页面,发现分享给微信好友的链接,不显示标题和缩略图 步骤一:安装 ...

  6. 【分享】如何才能简洁高效不失优雅的爆破ZIP文件?

    0x01 前言 在CTF比赛中,压缩包密码的爆破一直是一个热门话题.在这个过程中,简洁高效的方法是至关重要的.本文将介绍一些实用的技巧和工具,帮助您高效地爆破ZIP文件密码,而不失优雅.我们将探讨一些 ...

  7. ois七层模型与数据封装过程

    一,ois七层模型 一,ois七层模型1 为什么要分层2 七层模型3 七层总结二,协议,端口,的作用2.1协议作用2.2tcp/udp的区别2.3ARP 协议的作用2.4客户端与服务端的作用2.5ic ...

  8. Excel中的数值四舍五入方法详解

    在日常工作和数据处理中,我们经常需要对数值进行四舍五入操作.Excel作为一款强大的电子表格软件,提供了多种方法来实现数值的四舍五入.本文将介绍Excel中常用的四舍五入函数及其基本使用方法. ROU ...

  9. word2010中统一调整表格格式

    word中统一调整表格格式基本思路是: 1.选中所有的表格. 2.再对表格格式调整.    选中所有表格需要用到宏,操作很简单,具体操作如下: (1)工具栏"视图"下右下角&quo ...

  10. 安卓APK资源混淆加密重签名工具 (安卓APK加固, 代码混淆, 资源混淆,保护APK)

    安卓APK资源混淆加密重签名工具,可以对安卓APK文件的代码和资源文件进行混淆加密处理,可以对安卓APK文件进行加固,对代码和资源文件进行混淆,重新签名等功能. 可以保护APK,增加破解难度等功能. ...