首页 > 其他分享 >​OpenGL 学习系列---坐标系统

​OpenGL 学习系列---坐标系统

时间:2022-10-08 22:03:28浏览次数:72  
标签:OpenGL 投影 裁剪 矩阵 --- 坐标 空间

在前面​​绘制基本图形​​中,遇到了很明显的问题,圆形不像圆形,正多边形不像正多边形?就像下面图形一样:

​OpenGL 学习系列---坐标系统_设备坐标不规则的形状

好好的正五边形却东倒西歪的,这就是因为我们前面的绘制都是把它当成 二维 的绘制,而在 OpenGL 中却是绘制 三维的。在二维和三维之间还有个转换,而之前为了方便学习则忽略了这个转换,现在就要开始理解它了 —— ​​坐标系统​​!!

坐标系统

​OpenGL 学习系列---坐标系统_归一化_02三维坐标系

在立体几何的坐标系里面定义一个点的位置,需要 x、y、z 三个坐标轴的值,而在 OpenGL 中绘制 3D 物体也是需要的。

在绘制基本形状时,只是定义了 x、y 轴的坐标,这样 z 轴的坐标就默认为 0 了。

OpenGL 将定义好的坐标轴的值转换为实际绘制的坐标,需要经过五个坐标系统的转换。

如下图所示:

​OpenGL 学习系列---坐标系统_设备坐标_03坐标转换

这里面涉及到了五个坐标空间和三个转换矩阵:

空间:

  • 局部空间(Local Space)
  • 世界空间(World Space)
  • 观察空间(View Space)
  • 裁剪空间(Clip Space)
  • 屏幕空间(Screen Space)

矩阵:

  • 模型矩阵(Model Matrix)
  • 视图矩阵(View Matrix)
  • 投影矩阵(Projection Matrix)

根据流程图,每个坐标空间的转换都需要一个转换矩阵来完成。

最后裁剪空间到屏幕空间的转换,就是将经过这一系列转换后的坐标映射到屏幕的坐标上,这一过程就不需要转换矩阵了。

在进入不同的坐标空间之前,需要先了解 OpenGL 的坐标系:

OpenGL 是一个右手坐标系,正 X 轴在右手边,正 Y 轴朝上,正 Z 轴穿过屏幕朝向你。

​OpenGL 学习系列---坐标系统_投影矩阵_04

与之相对的就是左手坐标系,其正 Z 轴穿过屏幕朝向里面了。

局部空间

局部空间坐标是 OpenGL 绘制坐标的起点,接下来所有的转换操作都是在局部空间坐标基础上进行的。

局部空间坐标就是我们自己定义的起始坐标点,是相对于原点 ​OpenGL 学习系列---坐标系统_归一化_05 的。

此时所在的空间就是局部空间,也就是说我们在局部空间里面定义物体的起始坐标。

世界空间

我们定义每一个坐标点都是在局部空间,相对于 ​OpenGL 学习系列---坐标系统_归一化_05 的。这样一来,当多个物体同时绘制时,就会扎堆了。

而世界空间就是当所有物体一起绘制、仍然相对于原点的、更大的一个坐标系。

局部空间和世界空间有点相像,可以在局部空间定义坐标系时就考虑到世界坐标系,避免多个物体绘制时出现扎堆现象。

当然还有更好的方法,就是使用模型矩阵(Model Matrix)。

使用模型矩阵,可以对物体进行位移、缩放、旋转。

这样的话就可以将物体从坐标原点移开,并且还能够进行一些相关操作,不用去考虑在局部空间来定义世界空间的坐标了。

观察空间

横看成岭侧成峰 远近高低各不同

当物体在世界空间中就位了,接下来就是要考虑从哪个方向和角度来观察物体了。

观察空间,又是 OpenGL 的摄像机,是将世界空间的坐标转化为摄像机的视角所观察到的空间坐标。

也就是说,在观察空间里,坐标原点不再是世界空间的坐标原点了,而是以摄像机的视角作为场景原点,这就不再是简单地进行平移、旋转了,而是切换到另一种坐标系里。

OpenGL 本身是没有摄像机的概念的,不过可以通过把场景中的所有物体往相反的方向移动来模拟出摄像机。这样就场景没动,而摄像机在移动。

要定义一个摄像机,或者说要定义一个摄像机视角为坐标原点的坐标系,需要:

  • 摄像机在世界空间中的位置
  • 摄像机观察的方向
  • 指向摄像机右测的向量
  • 指向摄像机上方的向量


​OpenGL 学习系列---坐标系统_归一化_07

如图,最终建立了一个以摄像机位置为原点的坐标系。

其中,蓝色箭头为摄像机坐标系中的 Z 轴,绿色箭头为摄像机坐标系中的 Y 轴,红色箭头为摄像机坐标系中的 X 轴。

而接下来要做的就是将物体在世界空间中的坐标转换到以摄像机视角为原点的观察空间坐标中。

这其中也需要用到一个转换矩阵:视图矩阵(View Matrix)。通过视图矩阵来切换坐标系。

裁剪空间

当物体坐标都位于观察空间后,接下来要做的就是裁剪。根据我们的需要来裁剪一定范围内的物体,而在这个范围之外的坐标就会被忽略掉。

裁剪空间实质上还是进行坐标的操作。

从观察空间到裁剪空间,需要用到:投影矩阵(Projection Matrix)。

投影矩阵会指定一个坐标范围,这个范围内的坐标将变换为​​归一化设备坐标​​ ,不在这个范围内的坐标就会被裁剪掉。

观察空间中的坐标经过投影矩阵的变换之后称为投影坐标,又叫做​​裁剪坐标​​。

说是裁剪坐标,其实是待裁剪,接下来的裁剪过程将由 OpenGL 来完成的。投影矩阵的变换,只是筛选出那些不需要被裁剪的坐标。

由投影矩阵创建的范围,是一个封闭的空间几何体,被称为​​视景体​​。

投影矩阵有两种不同的形式,创建的视景体也有两种样式。

正交投影

​OpenGL 学习系列---坐标系统_投影矩阵_08

正交投影会创建一个类似立方体的视景体。它由左、上、右、下 四个方向距离和近平面距离、远平面距离组成。四个方向距离定义了近平面和远平面的大小。而在近平面和远平面之外的坐标点就会被裁剪掉了。

​OpenGL 学习系列---坐标系统_投影矩阵_09

在场景中处于视景体内的物体会被投影到近平面上,然后再将近平面上投影出的内容映射到屏幕上。

它所用到的矩阵是正交投影矩阵。

​OpenGL 学习系列---坐标系统_归一化_10

由于正交投影是平行投影的一种,其投影线是平行的,所以投影到近平面上的图形不会产生真实世界中的​​近大远小​​的效果。因为正交投影没有把透视考虑进去,所以,远处的物体不会变小,这适用于一些特定的场合。

透视投影

​OpenGL 学习系列---坐标系统_投影矩阵_11

透视投影是能够产生​​近大远小​​效果的,就像我们人眼一样,看远处的物体就变得很小了。

它所用到的矩阵就是透视投影矩阵。

​OpenGL 学习系列---坐标系统_投影矩阵_12

透视投影也会创建一个视景体,类似于锥形。它同样也有着近平面距离和远平面距离,而且也是将近平面的内容映射到屏幕视口中,但不同与正交投影近平面和远平面大小相同,所以它的左、上、右、下距离都是相对于近平面的。

​OpenGL 学习系列---坐标系统_设备坐标_13

可以看到,透视投影的投影线互不平行,都相交于视点。因此,同样尺寸的物体,才会近处的投影出来大,远处的投影出来小。

透视除法

当坐标经过投影矩阵的变换到裁剪空间之后,紧接着就会进行​​透视除法​​的操作。

透视除法是在三维绘制中产生​​近大远小​​效果非常关键重要的一步。

在此之前要先来了解一下 OpenGL 中的 ​​w 分量​​。

OpenGL 坐标系中除了 x、y、z 坐标外,还有 w 分量,默认情况下都是 1 。而经过透视投影变换之后,w 分量不再是 1 了,正交投影不改变 w 分量。

而 OpenGL 进行裁剪,实质上是 GPU 进行裁剪的过程,就是将 x、y、z 坐标的绝对值与 w 分量绝对值进行比较,只要有一个分量的绝对值大于 w 的绝对值,就认为不在视景体内,会被裁剪掉。

经过裁剪之后,再进行透视除法。就是将 x、y、z 坐标分别除以 w 分量,得到新的 x、y、z 坐标。由于 x、y、z 坐标的绝对值都小于 w 的绝对值,所以得到新的坐标值都是位于 ​OpenGL 学习系列---坐标系统_设备坐标_14 的区间内的。此时得到的坐标,也就是​​归一化设备坐标​​。

归一化设备坐标是独立于屏幕的,而且它的坐标系用的是左手坐标系。

经过透视投影矩阵变换之后,每个坐标的 w 分量都不相同了,这样再经过透视除法操作,就会使得远处的物体看起来变小了。

屏幕空间

有了归一化设备坐标,最后一步就是将坐标投射到屏幕上,这一步是由 OpenGL 来完成的。

OpenGL 会使用 ​​glViewPort​​​ 函数来将归一化设备坐标映射到屏幕坐标,每个坐标都关联了屏幕上的一个点,这个过程称为​​视口变换​​。这一步操作不再需要变换矩阵了。

就这样,一个点的坐标就完成了从局部空间坐标 ​OpenGL 学习系列---坐标系统_归一化_15 到屏幕坐标 ​OpenGL 学习系列---坐标系统_投影矩阵_16 的转变。

​OpenGL 学习系列---坐标系统_投影矩阵_17

坐标的矩阵操作

点的坐标可以看作是一个向量,用 ​OpenGL 学习系列---坐标系统_投影矩阵_18 表示,而矩阵用 ​OpenGL 学习系列---坐标系统_设备坐标_19 表示。

那么,从 局部空间 -> 世界空间 -> 观察空间 -> 裁剪空间 ,四个空间的转换,需要用到三个转换矩阵,点从某个坐标系变换到另一个坐标系的时候都要左乘某个变换矩阵,最后裁剪空间的坐标可以表示如下:

​OpenGL 学习系列---坐标系统_设备坐标_20

而在着色器脚本中,​​gl_Position​​ 对应的也是 ​OpenGL 学习系列---坐标系统_投影矩阵_21  裁剪坐标。

有了裁剪空间坐标后,接下来的事情就交个 OpenGL 去完成裁剪和透视除法就好了。

图形适应宽高比

在文章一开始提到的,绘制的圆形变成了椭圆,绘制的正多边形却东倒西歪的,现在也能给出原因了。

默认情况下,局部空间、世界空间、观察空间、裁剪空间的坐标系都是重合的,都是以​OpenGL 学习系列---坐标系统_归一化_05为坐标原点。一开始只是给出了理想状态下的平面坐标点,并且定义着色器脚本如下:

1attribute vec4 a_Position;
2void main(){
3 gl_Position = a_Position;
4}

那么它经过一系列转换后,最后 OpenGL 用来裁剪的坐标还是我们定义的基于平面的坐标,只有​OpenGL 学习系列---坐标系统_设备坐标_23值,而 ​OpenGL 学习系列---坐标系统_投影矩阵_24 坐标默认为 0,​OpenGL 学习系列---坐标系统_设备坐标_25 坐标默认为 1 。经过透视除法后的归一化设备坐标依旧是​OpenGL 学习系列---坐标系统_设备坐标_23

而归一化设备坐标假定的坐标空间是一个正方形,但手机屏幕的视口却是一个长方形,这样的话,就会有一个方向被拉伸。同样的份数,但长度越长,导致每一份的长度也增加了,所以也就被拉伸了。

​OpenGL 学习系列---坐标系统_归一化_27

要解决这种问题,可以在归一化设备坐标上进行操作,将较长的一边乘以相应的比例系数,转化到同样的长度比上。

1// 1280 * 720 的宽高比
2aspect = width / height ;
3x = x * aspect
4y = y

​OpenGL 学习系列---坐标系统_归一化_28

这样一来,将较长的一边的比例放大了,取较短的那一边作为 1 的标准。

当然也可以在坐标转换成归一化设备坐标之前,也就是在投影时就把拉伸的情况考虑进去。

使用正交投影,再将物体的宽高投影到近平面上时,就把屏幕的宽高比例系数考虑进去,这样在转换成归一化设备坐标之前就已经完成了图形的宽高比适应。

这样的话,就需要修改着色器脚本语言,把投影矩阵考虑在内。

1attribute vec4 a_Position;
2uniform mat4 u_Matrix;
3void main(){
4 gl_Position = u_Matrix * a_Position;
5}

具体实操下一篇博客再写了。

参考

  1. 《OpenGL ES 应用开发实践指南》
  2. 《OpenGL ES 3.x 游戏开发》

具体代码详情,可以参考我的 Github 项目:

​https://github.com/glumes/AndroidOpenGLTutorial​

OpenGL 系列文章:


  1. ​OpenGL 系列---基础绘制流程​
  2. ​OpenGL 学习系列---基本形状的绘制​


​OpenGL 学习系列---坐标系统_设备坐标_29


标签:OpenGL,投影,裁剪,矩阵,---,坐标,空间
From: https://blog.51cto.com/u_12127193/5738753

相关文章

  • 基于python的基于Django工厂设备管理系统设计与实现-计算机毕业设计源码+LW文档
    caigoushenqing表注释:采购申请字段类型空默认注释id (主键)bigint(20)否主键addtimetimestamp否CURRENT_TIMESTAMP创建时间shebeimingchengvarchar(200)否设备名称shebeil......
  • Vue3组件库打包指南,一次生成esm、esm-bundle、commonjs、umd四种格式
    本文为Varlet组件库源码主题阅读系列第二篇,读完本篇,你可以了解到如何将一个Vue3组件库打包成各种格式上一篇里提到了启动服务前会先进行一下组件库的打包,运行的命令为:v......
  • Flink-动态表和持续查询
    在Flink中使用表和SQL基本上跟其他场景是一样的;不过对于表和流的转换,却稍显复杂。当我们将一个Table转换成DataStream时,有“仅插入流”(Insert-OnlyStreams)和“更新......
  • 推荐一个变量调试神器:go-spew
    今天给大家推荐的是一个可以将变量以一种非常友好的方式输出其完整的数据结构信息的工具:go-spew。 该包经过了全面的测试,测试覆盖率为100%。支持各种自定义配置,非常方便......
  • MYSQL-->存储过程与存储函数
    介绍存储过程是事先经过编译并且存储在数据库中的一段SQL语句的集合。调用存储过程可以简化应用开发人员的很多工作,减少数据在数据库和应用服务器之间的传输,有助于提高数......
  • ORA-39126: expdp
        Starting"SERVICEDESK"."SYS_EXPORT_SCHEMA_07": servicedesk/********@//11.32.18.110:1521/MOS7100directory=MOS7100_BACKUPDB_DIRdumpfile=servicedesk.......
  • ABC 271 F - XOR on Grid Path(搜索 meet in the mid)
    ABC271F-XORonGridPath题意:​ 给出20*20的地图,每个点上都有一个点权,保证为正整数。请问从(1,1)走到(n,n)且路径上所有点权异或和为0的路径有多少条。思路:​......
  • 2022-2023-1 20221425 《计算机基础与程序设计》第6周学习总结
    学期(如2022-2023-1)学号(如:20221300)《计算机基础与程序设计》第六周学习总结作业信息这个作业属于哪个课程<班级的链接>(如2022-2023-1-计算机基础与程序设计)这......
  • msf-地理位置获取
    相关内容一、GPS简介物理位置定位:根据IP的定位不准确,容易被欺骗,网上有很多IP伪造技术,所以定位肯定也不准确。GPS全球定位系统:(使用最广泛)GPS是英文GlobalPositioningSyst......
  • ORA-1536: space quota exceeded for tablespace
    Error: ORA1536Text:  spacequotaexceededfortablespace"<name>"-------------------------------------------------------------------------------Cause:......