openGL中的坐标系
Posted 音视频开发老舅
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了openGL中的坐标系相关的知识,希望对你有一定的参考价值。
openGL中使用的是右手坐标系
- 右手坐标系:伸开右手,大拇指指向X轴正方向,食指指向Y轴正方向,其他三个手指指向Z轴正方向
-
左手坐标系:伸开左手,大拇指指向X轴正方向,食指指向Y轴正方向,其他三个手指指向Z轴正方向
两者的区别主要是两者Z轴的方向是相反的
一、坐标系
openGL中主要的几种坐标系
- 世界坐标系
以屏幕中心为原点(0,0,0),当你面对屏幕时,右边是X正轴,上方是Y轴正轴,屏幕指向你的方向为Z轴正轴。窗口范围是从(-1,1),即屏幕左下角坐标为(-1,-1,0),右上角坐标为(1,1,0)。我们用这个坐标系描述物体及光源的位置
将物体放到场景中(平移、旋转等),这些操作就是坐标变换。openGL中提供了glTranslate / glScale / glRotate 三条坐标变换命令,利用变换矩阵运算命令,则可以实现任意复杂的坐标变换 - 惯性坐标系
由世界坐标系和物体坐标系联合理解
是物体坐标系的旋转,只是一个中间状态的描述,方便物体坐标系切换到世界坐标系 - 物体坐标系/局部空间坐标系
是以物体某一点为原点建立的坐标,该坐标仅对该物体适用,用来简化对物体各部分坐标的描述。物体放到场景中时,各部分经历的坐标变换相同,相对位置不变,可以视为一个整体 - 摄像机坐标系:观察者坐标系
以观察者为原点,视线的方向为Z轴的正方向。openGL管道会将世界坐标先变换到观察者坐标,然后进行裁剪。只有视线范围内的场景才会进行下一步的计算
本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,编解码,推拉流,srs)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓
坐标变换过程
openGL在每次顶点着色后,可见顶点都是标准化设备坐标,即每个顶点的x、y、z值都应该在-1到1之间,超出这个范围的顶点是不可见的
将坐标转换为标准化设备坐标,接着再转换为屏幕坐标的过程是分步进行的,这个过程中,物体的顶点在最终转换为屏幕坐标之前还会被转换到多个坐标系统。在这些过度的特定坐标系中,一些操作或运算更加方便。
变换矩阵
为了将坐标从一个坐标系变换到另一个,我们需要用到几个变换矩阵,常用有模型(Model)、观察(View)、投影(Projection)三个矩阵
坐标变换矩阵栈
栈顶是当前坐标变换矩阵,进入openGL管道的每个坐标都会先乘以这个矩阵,结果才是对应点在具体场景中的世界坐标。openGL中的坐标变换是通过矩阵运算完成的。变换中的矩阵乘法是叉乘,结果中包含方向,不符合交换律。
物体顶点的起始坐标是局部坐标,之后会转换为世界坐标、观察者坐标、裁剪坐标,最后以屏幕坐标的形式结束。可以参考下面这张图
二、坐标空间
局部空间
指的是物体所在的坐标空间,即对象最开始所在的地方,相对于这个物体来说是局部的
世界空间
是指物体的顶点相对于世界的坐标空间,物体分散在世界上摆放,则物体的坐标会从局部空间变换到世界空间。该变换是由模型矩阵(Model Matrix)实现的
- 模型矩阵 Model Matrix
是一种变换矩阵,可以通过对物体进行位移、缩放、旋转来将它置于它该在的位置和朝向。可以想象为你想一把椅子放在一个房间内,需要先将它缩小(它在局部空间中太大了),并将它移动到房间的某个位置,然后在y轴上往左右旋转一点以摆放整齐。就是将局部坐标变换到场景/世界中的不同位置
观测空间/观察空间
观察空间也被称为openGL的摄像机Camera,所以有时候也称为摄像机空间(Camera space) 或视觉空间(Eye space)。观察空间是将世界空间坐标转换为用户视野前方的坐标而产生的结果。也就是说,观察空间,就是从摄像机的视角所观察到的空间。
而这通常是由一系列的位移和旋转的组合来完成的,平移/旋转场景从而使特定的对象被变换到了摄像机的前方。
这些组合在一起的变换通常存储在一个 观察矩阵(View Matrix) 里,它被用来将世界坐标变换到观察空间。
裁剪空间
- 裁剪过程
一个顶点着色器运行的最后,openGL希望所有的坐标都能落在一个特定的范围内(-1,1),任何在这个范围外的点都应该被裁剪(Clipped)掉。被Clipped的就会被忽略,余下的坐标就将变为屏幕上可见的片段。这个空间称为裁剪空间
为了将顶点坐标从观察者坐标变换到裁剪空间,我们需要定义一个投影矩阵(Projection Matrix),它可以指定一个范围的坐标,投影矩阵会将在这个指定范围内的坐标变换为标准化设备坐标的范围(-1,1),所有在范围外的坐标不会被映射在-1到1的范围之间,所以会被裁剪掉
比如在每个维度上的-1000到1000,裁剪之后,坐标(12400,500,700)将是不可见的,因为其x坐标超出了范围(大于1000),它被转换为一个大于1的标准化设备坐标,所以被裁剪掉了
投影:将特定范围内的坐标转换为标准化设备坐标系的过程,称为投影
平截头体Frustum:由投影矩阵创建的观察箱被称为平接头体,每个出现在平截头体范围内的坐标都会最终出现在用户的屏幕上。(不太理解,是不是类似一个圆锥或圆柱,从中间用刀切开,那个切面的范围,就相当于平截头体??
- 透视除法 Perspective Division
- 一旦所有顶点被变换到裁剪空间,透视除法将会执行。这个过程中我们将位置向量的x,y,z分量分别除以向量的齐次分量w(深度)
透视除法是将4D裁剪空间坐标变换为3D标准化设备坐标的过程。这一步会在每一个顶点着色器运行的最后被自动执行。
之后,最终的坐标会被映射到屏幕空间中(使用glViewport中的设定),并被变换成片段
将观察坐标变换为裁剪坐标的投影矩阵可以分为两种不同的形式:正投影矩阵(Orthographic Projection Matrix)或透视投影矩阵(Perspective Projection Matrix)
透视投影
现实生活中,离观察者越远的东西看起来越小,这个现象称之为透视(Perspective)
openGL中的透视效果是由透视矩阵实现的。这个透视矩阵将给定的平截头体范围映射到裁剪空间,除此之外还修改了每个顶点坐标的w(深度)值,从而使得离观察者越远的顶点坐标的w分量越大。被变换到裁剪空间的坐标都会在-w到w之间。所以,一旦坐标在裁剪空间内之后,透视除法就会被应用到裁剪空间坐标上
正投影
当使用正投影时,每一个顶点坐标都会直接映射到裁剪空间中而不经过任何精细的透视除法(它仍然会进行透视除法,只不过w分量没有被改变,保持为1,所以不起作用)
三、将坐标系统组合在一起
我们为上述的每一个步骤都创建了一个变化矩阵:模型矩阵、观察矩阵、投影矩阵,一个顶点坐标将会根据以下过程被变换到裁剪坐标Vclip = Mpro * Mview * Mmodel * Vlocal
这一系列的矩阵变换需要从右向左,依次是M V P。最后的顶点应该被赋值到顶点着色器中的gl_Position,openGL会自动进行透视除法和裁剪
视口变换:openGL对裁剪坐标执行透视除法从而将它们变换到标准化设备坐标,然后openGL会使用glViewPort内部的参数来将标准化设备坐标映射到屏幕坐标,每个坐标都关联了屏幕上的一个点,这个过程称为视口变换
上述过程,裁剪之前的坐标变换可以由开发者参与,裁剪和裁剪后续的动作,是系统完成的
本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,编解码,推拉流,srs)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓
OpenGL中的坐标变换矩阵变换
OpenGL中六种常见坐标系:
1. Object or model coordinates(模型坐标系)
2. World coordinates(世界坐标系)
3. Eye (or Camera) coordinates(视坐标系)
4. Clip coordinates(裁剪坐标系)
5. Normalized device coordinates(归一化设备坐标系)
6. Window (or screen) coordinates(屏幕坐标系)
当然这六中坐标系只是经常在开发中涉及的,还有其他很多坐标系,比如在进行法向贴图的时候的切向坐标系等,此处只介绍这六中坐标系。
需要注意的是模型坐标系、世界坐标系、视坐标系以及裁剪坐标系任何时候都是右手坐标系。默认情况下这四个坐标系完全重合,默认这四个坐标系的原点都在屏幕中心,从原点向右为x轴正半轴,从原点向上为y轴正半轴,从原点垂直于屏幕向外是z轴的正半轴。如果进行了某些矩阵操作变换,那么这四个坐标系就很可能不在重合,不过这四个坐标系还是右手坐标系。NDC坐标系(归一化设备坐标系)是左手坐标系,其z轴方向与前四个坐标系的z轴方向相反。
Object or model coordinates(模型坐标系)
所谓的模型指的就是一个三维的物体。每个物体都有其自身的模型坐标系,也就是说物体A的模型坐标系为Coordinate System A,物体B的模型坐标系为Coordinate System B,二者的模型坐标系不同。模型坐标系是一个假想的坐标系,该坐标系与物体的相对位置始终是不变的。在进行了glLoadIdentity()之后,模型坐标系和世界坐标系是重合的,但是如果调用了glTranslate()或者glRotate(),模型坐标系就会进行相应的平移和旋转。该坐标系以物体的中心为坐标原点,物体的旋转、平移等操作都是以模型坐标系进行的。这时当物体模型进行旋 转、平移等操作时,模型坐标系也执行相应的旋转、平移等操作。在OpenGL中绘图时,都是首先通过glTranslate、glRotate等来改变模型坐标系与世界坐标系的相对位置,然后在模型坐标系中用glVertexf等绘图,比如绘 制了一个点glVertexf(1,2,3),那么坐标(1,2,3)是模型坐标系中的坐标,而不是世界坐标系中的坐标。
World coordinates(世界坐标系)
这个坐标系就是我们生活的真实的3D场景,在OpenGL中有且只有一个世界坐标系。模型坐标系中的模型坐标左乘模型矩阵之后会转化为世界坐标,假设模型坐标系中有一点P,其坐标为Pmodel(Xmodel,Ymodel,Zmodel),该点左乘模型矩阵Mmodel之后就会得到该点在世界坐标系中的坐标Pworld(Xworld,Yworld,Zworld),即
上式中的Mmodel就是模型矩阵,是一个4×4的矩阵组成如下图所示,
上式中的m3,m7,m11均为0,m15为1。 (m12,m13,m14)可以表示一个点P,P点表示的是物体的模型坐标系的坐标原点在世界坐标系中的位置。剩下的9个值可分为三组(m0,m1,m2)、(m4,m5,m6)、(m8,m9,m10),这三组可表示三个向量vector1、vector2、vector3,而且这三个向量都是归一化向量,也就是说向量长度为1。其中vector1表示的是物体的模型坐标系的x轴正半轴在世界坐标系中的方向,vector2表示的是物体的模型坐标系的y轴的正半轴在世界坐标系中的方向,vector3表示的是物体的模型坐标系的z轴正半轴在世界坐标系中的方向。由此可以看出模型矩阵包含了所有模型坐标系的信息(模型坐标系的原点在世界坐标系中的位置以及模型坐标系的三个坐标轴在世界坐标系中的方向)。由于模型坐标系的三个坐标轴互相两两垂直,所以vector1、vector2、vector3互相垂直。那么根据数学定义由其中任意两个向量相互叉乘即可得到另一个向量,即有如下三个关系式:
vector1 叉乘 vector2 = vector3;
vector2 叉乘 vector3 = vector1;
vector3 叉乘 vector1 = vector2;
又因为vector1、vector2和vector3都是单位向量,那么可知:
m0*m0 + m1*m1 + m2*m2 = 1;
m4*m4 + m5*m5 + m6*m6 = 1;
m8*m8 + m9*m9 + m10*m10 = 1;
根据上述理论,假设我们已知物体的模型坐标的原点在世界坐标系中的坐标为P,物体的模型坐标系的x、y、z三个坐标轴的正半轴在世界坐标系中的方向分别为vector1、vector2、vector3,且这三个向量都是归一化向量。那么根据这些信息我们就直接可以写出物体的模型矩阵,如下所示:
Eye (or Camera) coordinates(视坐标系)
我们绘制的图形最终要呈现在视坐标系中,视坐标系是右手坐标系的。那么什么是视坐标系呢?视坐标系就是Camera坐标系。现在打个比 方,你站在世界坐标系中,你的眼睛就是Camera,你的眼睛平视前方,并且保持视线方向与头顶的朝向互相垂直。现在你的眼睛就是视坐标系的坐标原点,你的视线方向就是视坐标系Z轴的负半轴方向,你的头顶的朝向就是视坐标系的Y轴正半轴方向,与YOZ相互垂直向右的指向就是X轴正半轴的方向,X轴正半轴可由Y与Z叉乘得到,Camera的视坐标系也可以称为uvn坐标系,对应着世界坐标系的XYZ三个轴。默认情况下,视坐标系与世界坐标系是重合的。需要注意的是Camera本身就是一种普通的物体,即Camera就是一种模型,自然就有了前面所述的模型坐标系。不过Camera又是一种有点特殊的模型,为什么这么说呢?因为正常的模型都会在三维空间中画出来,但是一般情况下我们不会在三维空间中把Camera画出来(当然也可以将其在3D空间中画出来),所以一般情况下我们可以把Camera看作是透明的模型,但是上述模型坐标系的一系列理论都适用于Camera。 我们在开头就说了每次点从某个坐标系变换到另一个坐标系的时候都要左乘某个变换矩阵,从世界坐标系变换到视坐标系的时候所要左乘的变换矩阵我们称之为视点矩阵,需要特别注意的是视坐标系并不神秘,其实视坐标系就是Camera的模型坐标系。在一般情况下,3D空间中只有一个Camera,那么也就是只有一个视坐标系,视坐标系就是Camera的模型坐标系。既然视坐标系就是Camera的模型坐标系,那么视坐标系中某点Pview(Xview,Yview,Zview)就是Camera的模型坐标系中点Pcamera_model(Xcamera_model,Ycamera_model,Zcamera_model),即
世界坐标系中的点左乘视点矩阵即可得到视点坐标系中坐标,那么有如下定义:
当然又因为Pview就是Pcamera_model,所以也有如下定义:
那么视点矩阵Mview如何计算呢?
我们上面说过Camera也是一种模型,其自身也有模型坐标系,假设我们已知Camera的模型坐标系的原点在世界坐标系中的坐标以及Camera的模型坐标系的三个坐标轴的正半轴在世界坐标系中的方向,那么我们就知道了Camera的模型矩阵Mmodel。
我们已知将世界坐标系中的点转换到视坐标系中时有如下定义:
Pview = Mview•Pworld,
我们又根据模型坐标系中的点转换到世界坐标系中的定义得知:
Pworld = Mcamera_model•Pcamera_model,所以将Pworld的值带入上式得
Pview = Mview•Pworld = Mview•(Mcamera_model•Pcamera_model) = (Mview•Mcamera_model)•Pcamera_model
即Pview = (Mview•Mcamera_model) •Pcamera_model,又已知Pview = Pcamra_model,那么则得到
Pcamera_model = (Mview•Mcamera_model) •Pcamera_model,所以(Mview•Mcamera_model)为单位矩阵,也就是说Mview与Mcamera_model互为逆矩阵,所以
现在视点矩阵就计算完了,就是Camera的模型矩阵的逆矩阵。
现在大家可以回想一下OpenGL中常用的glLookAt(cameraPoint,targetPoint,upDirection)方法,该方法用于设置camera,其中cameraPonint是摄像机在世界坐标系中位置,targetPoint是摄像机观察的世界坐标系中的某点,upDirection指的是摄像机的头顶朝向在世界坐标系中的方向。我们可以根据这三个参数完全推算出camera的模型矩阵,计算过程如下:
摄像机的模型矩阵由以上16个数据组成,根据我们之前所述,m3、m7、m11都为0,m15为1。我们根据targetPoint可以得知m12=targetPoint.x, m13= targetPoint.y, m14= targetPoint.z,我们根据targetPoint和cameraPoint这两个点就能得到camera的视线方向,视线方向的反方向就是Camera的模型坐标系的z轴正半轴在世界坐标系中的方向,假设该方向的归一化向量是vector3,那么m8=vector3.x, m9=vector3.y, m10=vector3.z。upDirection就是Camera的模型坐标系的y轴正半轴在世界坐标系中的方向,假设该方向的归一化向量是vector2,那么m4=vector2.x, m5=vector2.y, m6=vector2.z。由于三个坐标轴互相垂直,那么Camera的模型坐标系的x轴正半轴在世界坐标系中的归一化后的方向vector1 = vector3 叉乘 vector2。则m0=vector1.x, m1=vector1.y, m2=vector1.z。这样我们根据glLookAt(cameraPoint,targetPoint,upDirection)中的三个参数完全推出了Camera的模型矩阵,同时我们便可以计算出视点矩阵Mview为Camera模型矩阵的逆矩阵。也就是说glLookAt方法确定了Camera的模型矩阵,而且确定了3D空间的视点矩阵。
Clip coordinates(裁剪坐标系)
现实生活中的场景都满足“近大远小”的特点,但是视点坐标系中并不满足“近大远小”的特点,视点坐标系中的物体远近都一样大,为了将上一步得到的视点坐标转换为符合“近大远小”特点的坐标,我们需要将视点坐标左乘一个变换矩阵,我们称这个矩阵为投影矩阵,相乘得到的坐标称为投影坐标,但一般情况下我们更多地称其为裁剪坐标(为什么叫这个名字下面会解释)。Pclip = Mproj•Pview。裁剪坐标系也是右手坐标系。
首先我们会定义一个视景体,位于视景体内的物体我们才会看到,不在视景体内的物体就会被裁剪抛弃掉。视景体如下所示:
视景体是由六个参数构成的: left、right、top、bottom以及near和far。视景体是存在于视坐标系中的,这6个参数也是视坐标系中的。near表示的是视坐标系的坐标原点近裁剪面的距离,far表示的是视坐标系的坐标原点到远裁剪面的距离。[left,right]对应近裁剪面的x的值域,[bottom,top]对应近裁剪面的y的值域,一般情况下left=-right、bottom=-top。根据这六个参数就可以构造投影矩阵了。此处我们暂时不讨论如何构建该矩阵。
在GLSL的顶点着色器中,我们一般都要计算设置gl_Position的值。一般情况下该值这样设置: gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition,1.0);
其中aVertexPosition为模型坐标,uMVMatrix为模型视点矩阵,即为视点矩阵与模型矩阵的乘积,则uMVMatrix = uViewMatrix * uModelMatrix。uPMatrix是投影矩阵,gl_Position就是经过了投影的裁剪坐标,也就是说gl_Position所在的坐标系就是投影坐标系。现在我们来解释一下这个坐标系为什么叫做“裁剪坐标系”,看看哪里来的“裁剪”二字:
需要特别注意的是,模型坐标系、世界坐标系、视点坐标系中的第四个分量w都是1,但是经过了投影变换之后的坐标的w分量不再为1,这时候就可以发挥w分量的作用了。在我们手动编程计算完gl_Position之后,进入GPU自身的流水管线,GPU会根据裁剪坐标gl_Position中xyz分量与w分量绝对值的大小进行比较进行裁剪。具体的过程是:GPU依次将gl_Position中x、y、z的绝对值与w的绝对值分别比较,只要有一个分量的绝对值大于w的绝对值,GPU就认为该点不在视景体内,就会被裁减掉,也就是说裁剪的过程是GPU自己进行的,没有被裁减掉的坐标xyz分量的绝对值都小于w的绝对值。所以现在应该知道经过投影之后的坐标是为了让GPU进行裁剪用的,所以才叫做“裁剪坐标”。
Normalized device coordinates(归一化设备坐标系)
裁剪坐标在经过GPU自动的裁剪之后会过滤掉那些位于视景体之外的坐标,只保留位于视景体内的坐标,然后GPU会自动进行投影除法(projective division)。
具体的过程是:会将齐次坐标转换为普通的三元的坐标(只有xyz,无w),会让裁剪坐标(裁剪坐标也是齐次坐标,包含w信息)中的xyz依次除以w,得到新的xyz,新的xyz就是归一化后的坐标,即归一化设备坐标(Normalized Device Coordinates),NDC坐标存放于一个边长为2的立方体坐标系中,NDC坐标是三分量的坐标,只包含xyz信息,不再包含w信息。归一化后的NDC坐标中的x、y、z的取值范围都是[-1,1]。x、y的取值范围是[-1,1],所表达的含义是将屏幕的中心点对应于[0,0],屏幕左下点对应[-1,-1],屏幕右上点对应于[1,1],x和y能够表示出该点相对于屏幕中心原点的位置。NDC坐标中的z表示了深度信息,取值范围也是[-1,1]。
需要特别注意的是NDC坐标系与裁剪坐标系相比其Z轴方向发生了翻转,也就是说NDC坐标系是左手坐标系,这个非常重要。而且其xyz的取值都是[-1,1]。Xndc与Yndc比较好计算,就是简单的线性比例计算而已。那么Zndc如何计算呢?
Zndc表示的是深度信息,视景体的近裁剪面的Zndc为-1,视景体的远裁剪面的Zndc为1。Zndc与Zview并不是线性的关系,两者的具体关系是:
其中far指的是视坐标系中坐标原点到远裁剪面的距离,near指的是视坐标系中坐标原点到近裁剪面的距离,Zveiw是视坐标系中的Z值。这样就可以根据Zview计算出Zndc。
当位于近裁剪面时,Zview=-near,则计算出的Zndc=-1;
当位于远裁剪面时,Zview=-far,则计算出的Zndc=1。
那么Zview为何值时,Zndc为0呢?注意肯定不是近裁剪面与远裁剪面的中间点。
根据上面的公式我们可以计算出当Zndc=0时,
Window (or screen) coordinates(屏幕坐标系)
最后系统需要根据NDC坐标将其画到我们的屏幕上,此处不再通过矩阵来完成了,而是通过调用命令glViewPort(x, y, width, height)。
x、y指定了视口矩形的左上角点,默认值是(0,0);
width、height指定了视口的宽度和高度。当OpenGL上下文第一次绑定到window时,width和height被默认设置为window的宽度和高度。
glViewport指定了x、y从NDC坐标系(归一化设备坐标系)到屏幕坐标系的仿射变换。如果(Xndc,Yndc)是归一化坐标,那么屏幕坐标(Xwindow,Ywindow)计算如下:
一般情况下,在调用glViewPort时,我们一般使用glViewPort(0, 0, width, height),也就是前两个偏移坐标都为0,所以上式简化为:
在片源着色器中有一个内建的输入vec4变量gl_FragCoord,gl_FragCoord表示的是片元的坐标位置,包含了片元的窗口相对坐标值(x, y, z, 1/w)
其中此处的gl_FragCoord.x和gl_FragCoord.y分别就是上述计算的Xwindow和Ywindow,即表示相对于屏幕左上角的屏幕坐标位置。屏幕坐标系的左上角为坐标原点,向右为X轴正半轴,向下为Y轴正半轴。gl_FragCoord.z表示的是片元的深度值,默认情况下,在片源着色器中gl_FragCoord.z的深度取值范围是[0, 1],注意,NDC坐标系中的z的取值范围是[-1, 1],z值的坐标范围之所以能从NDC坐标系中的[-1,1]转变到屏幕坐标系中的[0, 1],是因为OpenGL会根据函数glDepthRange(nearVal, farVal)中nearVal与farVal的值,将NDC坐标系中z从[-1, 1]映射到[nearVal, farVal],这一映射就是简单地线性变换。其中,如果没有主动调用过glDepthRange,那么OpenGL就认为nearVal就是0,farVal就是1。 在NDC坐标系中,近裁剪面的z值为-1,远裁剪面的z值为1,根据nearVal与farVal,将近、远裁剪面的z值分别映射到了nearVal与farVal,默认情况下在屏幕坐标系中,原近裁剪面的深度z就变成了0,原远裁剪面的深度z就变成了1。 gl_FragCoord中的第四个分量是存储了“投影除法”的相关信息1/w。
至此我们就完成了将一个模型坐标转换成屏幕坐标的一系列变换。
以上是关于openGL中的坐标系的主要内容,如果未能解决你的问题,请参考以下文章
实时音视频互动系列(下):基于 WebRTC 技术的实战解析
新的Google Lyra音频编解码器对实时视频流意味着什么?