先上结论:
Unity 使用的是左手坐标系,threejs和WEBGL是右手坐标系
一、概念
关于左右手坐标系的概念,可以参考冯乐乐 unity shader入门精要
第4章 学习Shader所需的数学基础(上)(坐标系、点和矢量)
1.左手坐标系和右手坐标系
为什么在三维笛卡儿坐标系中要区分左手坐标系和右手坐标系,而二维中就没有这些烦人的事情呢?
这是因为,在二维笛卡儿坐标系中, x 轴和y 轴的指向虽然可能不同,就如我们在图4.4中看到的一样。但我们总可以通过一些旋转操作来使它们的坐标轴指向相同。以图4.4 中OpenGL和DirectX 使用的坐标系为例,为了把右侧的坐标轴指向转换到左侧那样的指向,我们可以首先对右侧的坐标系顺时针旋转180。此时它的y 轴指向上,而x 轴指向左。然后,我们再把整个纸面水平翻转一下,就可以把x 轴翻转到指向右了,此时左右两侧的坐标轴指向就完全相同了。从这种意义上来说,所有的二维笛卡儿坐标系都是等价的。
但对于三维笛卡儿坐标系,靠这种旋转有时并不能使两个不同朝向的坐标系重合。例如,在图4.6 中,+z 轴的方向指向纸面的内部,如果有另一个三维笛卡儿坐标系,它的+z 轴指向纸面外部, x 轴和y 轴保持不变,那么我们可以通过旋转把这两个坐标轴重合在一起吗?答案是否定的。
我们总可以让其中两个坐标轴的指向重合,但第三个坐标轴的指向总是相反的。
也就是说, 三维笛卡儿坐标系并不都是等价的。因此,就出现了两种不同的三维坐标系: 左手坐标系和右手坐标系。如果两个坐标系具有相同的旋向性( handedness ),那么我们就可以通过旋转的方法来让它们的坐标轴指向重合。但是,如果它们具有不同的旋向性〈例如坐标系A 属于左手坐标系,而坐标系B 属于右手坐标系〉,那么就无法达到重合的目的。
那么,为什么叫左手坐标系和右手坐标系呢?和手有什么关系?这是因为,我们可以利用我们的双手来判断一个坐标系的旋向性。请读者举起你的左手,用食指和大拇指摆出一个“L”的手势,并且让你的食指指向上,大拇指指向右。现在,伸出你的中指,不出意外的话它应该指向你的前方(如果你一定要展示自己骨惊奇的话我也没有办法〉。恭喜你, 你己经得到了一个左手坐标系了!你的大拇指、食指和中指分别对应了+x 、+y 和+z 轴的方向,如图4.7 所示。
正如我们之前所说,左手坐标系和右手坐标系之间无法通过旋转来同时使它们的3 个坐标轴指向重合,如果你不信,你现在可以拿自己的双手来试验一下。
另外一个确定是左手还是右手坐标系的方法是,判断前向( forward ) 的方向。请读者坐直,向右伸直你的右手,此时右手方向就是x 轴的正向,而你的头顶向上的方向就是y 轴的正向。这时,如果你的正前方的方向是z 轴的正向,那么你本身所在的坐标系就是一个左手坐标系: 如果你的正前方的方向对应的是z 轴的负向,那么这就是一个右手坐标系。
除了坐标轴朝向不同之外,左手坐标系和右手坐标系对于正向旋转的定义也不同,即在初高中物理中学到的左手法则(left-hand rule ) 和右手法则(right-hand rule )。
2.正旋转
假设现在空间中有一条直线,还有一个点,找们希望把这个点以该直线为旋转轴旋转某个角度,比如旋转30度。读者可以拿一支笔当成这个旋转轴,再拿自己的手当成这个需要旋转的点,可以发现,我们有两个旋转方向可以选择。那么,我们应该往哪个方向旋转呢?
这意味着,我们需要在坐标系中定义一个旋转的正方向。在左手坐标系中,这个旋转正方向是由左手法则定义的,而在右手坐标系中则是由右手法则定义的。
在左手坐标系中,我们可以这样来应用左手法则:还是举起你的左手,握拳,伸出大拇指让它指向旋转轴的正方向,那么旋转的正方向就是剩下4 个手指的弯曲方向。在右手坐标系中,使用右手法则对旋转正方向的判断类似,如图4.9 所示。
从图4.9 中可以看出,在左手坐标系中,旋转正方向是顺时针的,而在右手坐标系中,旋转正方向是逆时针的。
3.左右手坐标系之间的转换
左右手坐标系之间是可以进行互相转换的。最简单的方法就是把其中一个轴反转,并保持其他两个轴不变。
对于开发者来说,使用左手坐标系还是右手坐标系都是可以的,它们之间并没有优劣之分。无论使用哪种坐标系,绝大多数情况下并不会影响底层的数学运算,而只是在映射到视觉上时会有差别(见练习题2 )。
这是因为,一个点或者旋转在空间内来说是绝对的。一些较真儿的读者可能会看不惯“绝对”这个词: “你怎么能忽略相对论呢?这世上一切都是相对的!”这些读者请容我解释。这里所说的绝对是说, 在我们所关心的最广阔的空间中,这些值是绝对的。例如我说,把你的书从桌子的左边移到右边,你不会对这个过程产生什么疑问, 此时我们关心的整个空间就是桌子这个空间,而在这个空间中,书的运动是绝对的。但是,在数学的世界中,我们需要使用一种数学模型来精确地描述它们,这个模型就是坐标系。一旦有了坐标系,每个点的位置就不再是绝对的,而是相对于这个坐标系来说的。这种相对关系导致,即便从数学表示上来说两种表示方式完全一样,但从视觉上来说是不一样的。
我们可以在奶牛农场的例子中体会左手坐标系和右手坐标系的分别。我们假设,妞妞想要到一个新的地方,因为那里的草很美味。妞妞知道到达这个目标点的“绝对路径”是怎样的,如图4.10 所示。
我们可以分别在一个左手坐标系和右手坐标系中描述这样一次运动, 即使用数学表达式来描述它。我们会发现,在不同的坐标系中描述这样同一次运动是不一样的,如图4.11 所示。
在左手坐标系中, 3 个坐标轴的朝向如图4.11 左图所示。妞妞首先向x 轴正方向平移1个单位,然后再向z 轴负方向移动4 个单位,最后朝旋转的正方向旋转60。。而在右手坐标系中,+z轴的方向和左手坐标系中刚好相反,因此妞妞首先向x 轴正方向平移1个单位(与左手坐标系中的移动一致),然后再向z 轴正方向移动4 个单位( 与左手坐标系中的移动相反) ,最后朝旋转的负方向旋转60。(与左手坐标系中的旋转相反)。
可以看出,为了达到同样的视觉效果(这里指把妞妞移动到视觉上的同一个位置),左右手坐标系在z 轴上的移动以及旋转方向是不同的。如果使用相同的数学运算(指均向z 轴某方向移动或均朝旋转正方向旋转等〉,那么得到的视觉效果就是不一样的。因此,如果我们需要从左手坐标系迁移到右手坐标系,并且保持视觉上的不变,就需要进行一些转换。读者可以参见本章最后的扩展阅读部分。
4.左右手坐标系与叉积
这个结果是怎么得到的呢?来,先举起你的右手。在右手坐标系中, a×b 的方向将使用右手法则来判断。我们先想象把手心放在了a 和b 的尾部交点处,然后张开你的手掌让手掌方向和a 的方向重合,再弯曲你的四指让它们向b 的方向靠拢,最后伸出你的大拇指! 大拇指指向的方向就是右手坐标系中a x b 的方向了。如果你实在不明白怎么摆放和扭动你的手,那么就看图4.28 好了.
同理,我们可以使用左手法则来判断左手坐标系中ax b 的方向。赶紧举起你的左手试试吧(你可能会发现这个姿势比较扭曲。〉!
需要注意的是,虽然看起来左右手坐标系的选择会影响叉积的结果,但这仅仅是“看起来”而己。从叉积的数学表达式可以发现,使用左手坐标系还是右手坐标系不会对计算结果产生任何影响,它影响的只是数字在三维空间中的视觉化表现而己。当从右手坐标系转换为左手坐标系时,所有点和矢量的表达和计算方式都会保持不变,只是当呈现到屏幕上时, 我们可能会发现, “咦,怎么图像反过来了! ”。当我们想要两个坐标系达到同样的视觉效果时,可能就需要改变一些数学运算公式, 这不在本书的范畴内。有兴趣的读者可以参考本章的扩展阅读部分。
那么,叉积到底有什么用呢?最常见的一个应用就是计算垂直于一个平面、三角形的矢量。另外,还可以用于判断三角面片的朝向。
二、Unity 使用的坐标系
1.模型空间和世界空间
对于一个需要可视化虚拟的三维世界的应用(如Unity )来说,它的设计者就要进行一个选择。对于模型空间和世界空间(在4.6 节中会具体讲解这两个空间是什么) , Unity 使用的是左手坐标系(注:threejs是右手坐标系)。这可以从Scene 视图的坐标轴显示看出来,如图4.12 所示。这意味着,在模型空间中,一个物体的右侧( right)、上侧( up )和前侧( forward )分别对应了x 轴、y 轴和z 轴的正方向。
2.观察空间
但对于观察空间来说, Unity 使用的是右手坐标系。观察空间,通俗来讲就是以摄像机为原点的坐标系。在这个坐标系中,摄像机的前向是z 轴的负方向,这与在模型空间和世界空间中的定义相反。也就是说, z 轴坐标的减少意味着场景深度的增加,如图4.13 所示。