OpenGL坐标系是左手坐标系还是右手坐标系?

110

相关内容:http://programmers.stackexchange.com/a/88776/12693 - Kos
5
这不完全取决于你在着色器中编写转换的方式吗?因此,这完全取决于您自己。 - jcoder
这只是我的个人看法,http://www.evl.uic.edu/ralph/508S98/coordinates.html,其中有一些自我解释的图片。 - rraallvv
你是否考虑更新你接受的答案? - Jonathan Mee
6个回答

151

这里有一些混淆。

enter image description here

OpenGL在物体空间和世界空间中是右手坐标系

但在窗口空间(也称为屏幕空间),我们突然变成了左手坐标系

这是怎么发生的

我们从右手坐标系到左手坐标系的方法是在glOrthoglFrustum投影矩阵中添加一个负的z缩放项。将z乘以-1(保持x和y不变)会改变坐标系的左右手性。

对于glFrustum,

enter image description here enter image description here

farnear应该是正数,且far>near。假设far=1000,near=1,则C=-(1001)/(999)=-1.002。

有关详细信息和图表,请参见此处

从正交的角度来看,glOrtho生成如下矩阵:

enter image description here

这里,leftrightbottomtop只是剪切平面(分别为左垂直、右垂直、底部水平、顶部水平)的坐标(resp)

然而,近平面远平面的指定方式不同near参数定义为

  • 近:到更近的深度裁剪平面的距离。如果该平面在观察者后面,则该距离为负。

和远:

  • zFar:到较远的深度裁剪平面的距离。如果该平面在观察者后面,则该距离为负。

这里有一个典型的规范视图体积

canonical

因为z乘数是(-2/(far-near)),负号有效地将z缩小了-1。这意味着在观察变换期间,“z”被转化为左手坐标系,而大多数人只是将OpenGL视为“右手”坐标系而不知情。

因此,如果您调用

glOrthof(-1, 1, -1, 1, 10, -10) ; // near=10, FAR=-10,

那么NEAR PLANE就在你前方10个单位的位置。 你在哪里? 在原点,x轴在右侧,y轴在你头上,鼻子朝着负z轴方向(这是默认情况下相机位于原点,指向负z轴,并且有一个上向量为(0,1,0)的设置)。 因此,近平面在z=-10的位置。 远平面在你身后10个单位,在z= +10的位置


1
"在gluLookAt中,前向量被取反了" - 你能确认这是事实吗?不是gluPerspective、glFrustum或glOrtho吗? - Kos
2
从剪辑空间开始,我们突然变成了左撇子,而不仅仅是窗口空间。 - legends2k
3
将文本从英语翻译成中文。仅返回翻译后的文本:整个答案得+1分,对于“我们如何从右手坐标系变为左手坐标系”,由于其不合理性将得到-2分。(注:该句话实际上是一种错误的说法。) - Christian Rau
3
gluLookAt计算中否定前向矢量与更改坐标空间的左右手性无关,这只是该矩阵的计算方式(实际上,在右手坐标系中+z确实是“向后”的)。 gluLookAt除了在右手坐标系中计算普通刚体变换(旋转+平移)之外,什么也不做。固定功能管道(通常也由着色器使用)使用的投影矩阵通过否定z分量来执行实际的左右手性变换,正如您在答案中已经指出的那样。 - Christian Rau
2
@bobobobo 你所说的是,如果只使用旋转和平移函数计算视图矩阵(我猜您不会指责其改变左右手性),而不使用gluLookAt,那么就不会发生基础变换,而gluLookAt确实会进行一次?绝对不会(因为您可能知道并非“每个人”都使用gluLookAt来计算视图矩阵),因为正如您应该知道的那样,gluLookAt仅仅是一堆旋转和平移调用(即使它们被伪装成“高层”的术语“基础变换”),没有涉及到任何反射。 - Christian Rau
显示剩余5条评论

36

默认情况下,规范化设备坐标是左手坐标系。

glDepthRange默认为[0, 1](近裁剪面,远裁剪面),使+z轴指向屏幕内部,+x向右,+y向上,这是一个左手坐标系。

将深度范围更改为[1, 0]将使系统变为右手坐标系。

引用answer from Nicol的先前回答:(删除线是我的工作,在下面解释)

我很惊讶没有人提到:OpenGL 也可以在左手坐标系下工作。至少,在使用着色器并使用默认深度范围时是这样的。一旦你放弃了固定管线,你就直接处理“裁剪空间”。OpenGL 规范将裁剪空间定义为四维齐次坐标系。当你跟随变换到规范化设备坐标,并进一步转换到窗口坐标时,你会发现这一点。窗口空间位于窗口像素的空间中。原点位于左下角,+Y 向上,+X 向右。这听起来非常像一个右手坐标系。但是 Z 坐标呢?默认深度范围(glDepthRange)将近 Z 值设置为 0,远 Z 值设置为 1。因此,+Z 离开观察者。这是一个左手坐标系。是的,你可以更改深度测试从 GL_LESS 到 GL_GREATER,将 glDepthRange 从 [0,1] 更改为 [1,0]。但 OpenGL 的默认状态是在左手坐标系下工作。必须从裁剪空间到窗口空间的所有变换都不否定 Z 轴。因此,顶点(或几何)着色器的输出裁剪空间是一个左手坐标系(有点像。它是一个四维齐次空间,因此很难确定其左右手性)。在固定管线中,标准的投影矩阵(由 glOrtho、glFrustum 等产生)都将从右手坐标系变换为左手坐标系。它们翻转了 Z 的含义;只需检查它们生成的矩阵即可。在眼空间中,+Z 向观察者移动;在后投影空间中,它向远离观察者的方向移动。我怀疑微软(和 GLide)根本没有在其投影矩阵中执行否定操作。
我删去了某个部分,因为它与我的研究结果不符。更改DepthRange或DepthFunc并使用ClearDepth(0)都可以,但是当两者同时使用时,它们会相互抵消回到一个左手坐标系。

深度范围不能是[1,-1]。我想你的意思是[1,0]。此外,这个问题之前已经被指出过(http://programmers.stackexchange.com/questions/17519/why-does-directx-use-a-left-handed-coordinate-system/88776#88776)。 - Nicol Bolas
4
为什么你认为纠正错误的陈述不值得?我已经花费了数小时来解决顶点坐标和矩阵旋转生成的问题,结果才发现默认的坐标系统实际上并不是右手系。 - hultqvist
1
问题是它是左手系还是右手系。这个答案比那个更详细,解释了另外两种正确获取右手系的方法,通过在矩阵变换中添加-1也是另一种方法。但如果你相信系统默认是右手系,那么这些都不重要,只有当你知道你从哪里开始改变时,才能改变某些东西。仍然。 - hultqvist
1
我觉得这个答案非常有启发性,而且它在搜索中出现了,所以对我来说是值得的。 - Learn OpenGL ES
@Kos 不仅是 DepthRange,而且它和 DepthFunc 一起使用,注意最后一段关于它们相互抵消的说明。 - hultqvist
显示剩余4条评论

20

仅使用标准设备坐标系

你应该只关注OpenGL只知道标准设备坐标系!并且那是一个左手坐标系。

无论你使用哪种坐标系 - 左手坐标系还是右手坐标系 - 都需要映射到标准设备坐标系。如果你愿意,完全可以将世界空间处理为左手坐标系。

为什么我们通常在世界空间中使用右手坐标系?

我认为这是传统的做法。就是这样。也许只是想与DirectX区分开来。


4
我认为这个观点没有被重复强调过。所有这些显示“建模转换”,“视图转换”,“透视转换”的图表都未能说明它们不是OpenGL的固有部分。即使没有着色器,也可以手动完成所有变换,有矩阵或无矩阵均可。 - Tom
6
如果重复一遍并包含"标准化设备坐标"这个术语可能会更好。 - Tommy
8
OpenGL比DirectX早几年出现,因此OpenGL当时肯定不是在试图与DirectX区分开来。DirectX的首席开发人员Alex St. John曾表示,微软选择左手坐标系“部分是基于个人喜好”和“一种任意选择”。 - Chris Nolet
1
这并不完全正确。OpenGL也知道剪裁坐标系,因为剪裁就是在其中发生的。然而,剪裁坐标系与NDC坐标系具有相同的左右手规则。 - plasmacel

11

《WebGL编程指南》一书中的篇幅之一是"WebGl/OpenGl: 左手坐标系还是右手坐标系?"

根据该书:

  • 实际上,大多数人使用右手坐标系

  • OpenGl 内部实际上使用左手坐标系

  • 内部更深层次,实际上是两者都不是。在最底层,OpenGl 不关心Z值。绘制物体的顺序决定了哪个物体会显示在顶部(先画三角形再画四边形,则四边形会覆盖三角形)。

我不完全同意"两者都不是",但这可能是一个哲学问题。


10
如果没有进行深度测试,你绘制物体的顺序会决定前后层叠关系(比如先画三角形再画四边形,四边形会覆盖三角形)。但如果使用深度测试,每个片段的深度值将与深度缓存中的值进行比较,如果未通过深度测试则该片段被丢弃。所以四边形是否覆盖三角形取决于它们的深度和深度函数的使用。 - chrisvarnz

4

Opengl显然是左手坐标系。你可能会看到很多教程声称相反,因为它们在投影矩阵中否定了z值。当最终的顶点在顶点着色器内计算时,它将转换从客户端传递过来的顶点(右手坐标系)为左手坐标系,然后将这些顶点传递给几何着色器和片段着色器。如果你在客户端使用右手坐标系,Opengl并不关心。它只知道标准化的左手坐标系。

编辑:如果你不相信我,可以在你的顶点着色器中添加一个平移矩阵进行实验,这样你就可以轻松地看到Opengl是否是左手坐标系。


我认为你说的OpenGL是右手坐标系是正确的,因为很多教程在投影矩阵中忽略了z轴,但我也不认为它本质上是左手坐标系。OpenGL规范中没有任何指示。唯一能找到的说它是左手坐标系的理由是默认深度测试函数是GL_LESS,这通常意味着具有较低z值的对象被视为更接近 - 但默认情况下深度测试本身已被禁用,所以这实际上取决于应用程序如何使用GL。 - brettwhiteman

3
通过使用OpenGL内置的投影和变换函数,观察屏幕上的移动遵循右手坐标系的规则。例如,如果视图前面的对象向正z方向平移,则该对象将向您移动。
深度缓冲区则相反,这就是NDC(归一化设备坐标)发挥作用的地方。如果将GL_LESS传递到glDepthFunc中意味着像素绘制时它们比深度缓冲区中的内容更接近您,则认为像素位于左手坐标系中。
还有一个坐标系,那就是视口!视口的坐标系使得+x指向右侧,+y指向下方。我认为到这个点为止,左右手性已经无关紧要了,因为我们在这个点只涉及x、y。
最后,gluLookAt在底层必须否定视线向量。由于数学假设向物体看去的矢量朝着正方向,而摄像机朝下看-z,因此必须否定视线向量,以便其与摄像机对齐。
可以思考一下:将右手坐标系的z方向称为前向矢量并不合理 : )。我认为微软在设计Direct3D时已经意识到了这一点。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接