首页 > 其他分享 >OpenGL 坐标系统详解

OpenGL 坐标系统详解

时间:2023-11-07 17:27:01浏览次数:42  
标签:glm OpenGL 0.5 详解 坐标 空间 GL 我们

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之间)都裁剪掉,这会导致一个你在游戏中很熟悉的视觉效果:在太过靠*一个物体的时候你的视线会直接穿过去。

 

 

标签:glm,OpenGL,0.5,详解,坐标,空间,GL,我们
From: https://www.cnblogs.com/rmb999/p/17815403.html

相关文章

  • ADC-过零检测详解
    ADC-过零检测详解1、反电动势波形的起源下图展示了内转子磁极的磁感应强度B的分布情况。定义磁感应强度方向向外为正在0°的时候,处于正反方向交界处,磁感应强度为零;然后开始线性增加,在A点时达到最大然后一直保持恒定值不变,直到B点开始下降,到180°的时候下降到零。然后开始负......
  • postgis导入shp数据指空间坐标系的方法
    转自:https://www.jianshu.com/p/2e3f31b9b9031、通过postgis导入界面进行设置 2、通过sql语句进行设置SELECTUpdateGeometrySRID('表名','geom',4326);执行成功后可在geometrycolomns里这个表的srid列变为4326 ......
  • 2、Text组件详解
    TextStyle的参数 //代码块importMimport'package:flutter/material.dart';voidmain(){runApp(MaterialApp(home:Scaffold(appBar:AppBar(title:constText("你好Flutter")),body:constMyApp(),),));}//代码块statelessWclassMyAppexten......
  • vmstat命令详解
    各种unix平台下iostat与vmstst说明vmstat是VirtualMeomoryStatistics(虚拟内存统计)的缩写,是实时系统监控工具。该命令通过使用knlist子程序和/dev/kmen伪设备驱动器访问这些数据,输出信息直接打印在屏幕。vmstat反馈的与CPU相关的信息包括:(1)多少任务在运行(2)CPU使用的情况(3)CPU收到......
  • matlab 对数坐标
    在很多工程问题中,通过对数据进行对数转换可以更清晰地看出数据的某些特征,在对数坐标系中描绘数据点的曲线,可以直接地表现对数转换.对数转换有双对数坐标转换和单轴对数坐标转换两种.用loglog函数可以实现双对数坐标转换,用semilogx和semilogy函数可以实现单轴对数坐标转换.logl......
  • 神经网络基础篇:详解向量化逻辑回归(Vectorizing Logistic Regression)
    向量化逻辑回归讨论如何实现逻辑回归的向量化计算。这样就能处理整个数据集,甚至不会用一个明确的for循环就能实现对于整个数据集梯度下降算法的优化首先回顾一下逻辑回归的前向传播步骤。所以,如果有\(m\)个训练样本,然后对第一个样本进行预测,需要这样计算。计算\(z\),正在使......
  • Azure Data Factory(十)Data Flow 组件详解
    一,引言随着大数据技术的不断发展,数据处理和分析变得越来越重要。为了满足企业对数据处理的需求,微软推出了AzureDataFactory(ADF),它是一个云端的数据集成服务,用于创建、安排和管理数据工作流。在本文中,我们将重点介绍AzureDataFactory的数据流(DataFlow),以及它如何帮助......
  • 【Redis使用手册】一年多来redis使用markdow笔记总结,第(2)篇:Redis命令操作详解
    Redis是一个高性能的key-value数据库。本文会让你知道:什么是nosql、Redis的特点、如何修改常用Redis配置、写出Redis中string类型数据的增删改查操作命令、写出Redis中hash类型数据的增删改查相关命令、说出Redis中list保存的数据类型、使用StrictRedis对象对string类型数据......
  • WM_CLOSE、WM_DESTROY、WM_QUIT及各种消息投递函数详解
     对WM_CLOSE、WM_DESTROY、WM_QUIT及各种消息投递函数的功能及区别做出了分析比对,有助于读者更好的对消息投递函数加以理解。详情如下:一、WM_CLOSE、WM_DESTROY、WM_QUIT区别WM_CLOSE:关闭应用程序窗口WM_DESTROY:关闭应用程序WM_QUIT:关闭消息循环只有关闭了消息循环,应用程序......
  • Jwt生成token详解
     publicStringcreateTokenByBao(StringuserId){Datedate=newDate();Stringtoken=Jwts.builder()//创建一个JWT构建器,用于创建和配置JWT。.setSubject(userId)//主题--生成token所需内容设置.setIssuedAt(date)//签发时间......