左右手坐标系和相关定则的总结

左手坐标系和右手坐标系是三维空间下两种不同的坐标系,而且无法通过旋转将左手坐标系转换到右手坐标系。与其相对应的,有左手定则和右手定则,主要是用来确定叉积的朝向或者说旋向。
首先,规定二维坐标,X轴朝右、Y轴朝上,推广到三维空间,需要确定的是Z轴是朝前还是朝后。

一、左手坐标系

所谓左手坐标系,指的是通过左手来确定的一个三维空间坐标系。

1.1 确定左手坐标系的方式

下面总结了三种可以确定左手坐标系的方法。

1.1.1 拇指、食指、中指相互垂直确定法


如图,伸出左手,拇指朝上代表Y轴、食指朝前代表Z轴、中指朝右代表X轴。注意,中指这个时候是只能往右边弯曲的。

1.1.2 左手定则确定法

伸出左手,手指朝着右边X轴,握向Y轴,这个时候拇指指向的方向就是Z轴(朝前)。

1.1.3 人站立的正面朝向确定法

人朝前站立着,右手伸出的朝向是X轴,头顶的方向是Y轴,面向Z轴。

1.2 左手定则

假设,叉乘计算,C=A叉乘B。如何确定在C的朝向了?如果A和B都在左手坐标系下,那么使用左手定则来确定C的朝向。
类似1.1.2,伸出左手,手指朝着A,握向B,这个时候拇指指向的方向就是C。

二、右手坐标系

2.1 确定右手坐标系的方式

2.1.1 拇指、食指、中指相互垂直确定法

参考1.1.1,伸出右手,拇指朝上代表Y轴、食指朝前代表Z轴、中指朝左代表X轴。注意,中指这个时候是只能往左边弯曲的。
但是,我们一般假定X轴朝右,因此需要握着Z轴旋转180度。这个时候,拇指朝上代表Y轴、食指朝后代表Z轴、中指朝右代表X轴。注意,左右手坐标系旋转后不会改变。

2.1.2 左手定则确定法

伸出右手,手指朝着右边X轴,握向Y轴,这个时候拇指指向的方向就是Z轴(朝后)。

2.1.3 人站立的正面朝向确定法

人朝前站立着,右手伸出的朝向是X轴,头顶的方向是Y轴,背后的是Z轴。

2.2 右手定则

类似1.1,如果A和B都在,右手坐标系下,那么使用右手定则来确定C的朝向。
类似1.1.2,伸出右手,手指朝着A,握向B,这个时候拇指指向的方向就是C。
因此,左手定则和右手定则的区别是使用左手还是右手。

三、图形API的左右手坐标系

图形管线中,存在多个坐标系,每个坐标系都可以使用左手或者右手坐标系。下面按照,物体坐标系->世界坐标系->摄像机坐标系->裁剪坐标系->窗口坐标系来说明。

3.1 OpenGL

OpenGL默认是右手坐标系。不过到了窗口坐标系,OpenGL使用的是左手坐标系。为什么了?因为OpenGL的深度范围是[0,1],而且是摄像机越远,深度越大,这就是左手坐标系啦
由于物体坐标系、世界坐标系、摄像机坐标系都是右手坐标系,但是窗口坐标系是左手坐标系,那么投影矩阵就需要乘以右手坐标系变换到左手坐标系这个变换,也就是Z变换成-Z。不过这个变换也可以放在摄像机坐标系,也就是MVP的V中。现在假定,都乘到P中了。
最终结论是:物体坐标系、世界坐标系、摄像机坐标系是右手坐标系;裁剪坐标系和窗口坐标系是左手坐标系,窗口坐标系实际上只是裁剪坐标系进行齐次除法后再平移缩放而已。

3.2 DirectX

DirectX默认是左手坐标系。
类似3.1,物体坐标系、世界坐标系、摄像机坐标系是左手坐标系。注意,DirectX的窗口坐标系是以左上角为原点的,深度是朝前的,那么跟OpenGL的反过来,是右手坐标系。
因此,裁剪坐标系和窗口坐标系是右手手坐标系。投影变化同样要乘以,右手坐标系变换到左手坐标系这个变换,也就是Z变换成-Z。

3.3 Vulkan

Vulkan的窗口坐标系和DirectX的一致,因此推测其余坐标系和DirectX的一致。

3.4 Metal

Vulkan的窗口坐标系和DirectX的一致,因此推测其余坐标系和DirectX的一致。

看来只有,历史遗留的奇葩OpenGL的窗口坐标系,原点在左下角啊。原点在哪,这个跟纹理的v坐标是否需要取反也有关系。

四、游戏引擎的左右手坐标系

游戏引擎中,物体和世界坐标系是固定的,对于所有的图形API都会一样。

4.1 Unity


根据上图,Unity的物体和世界坐标系可以推测都是左手系。

根据上图,出自Shader入门精要,Unity的窗口坐标系和OpenGL的一致,是左手系。但是摄像机空间变换到了右手系。那么,在V中需要乘以Z到-Z的变换。同时,P中再乘以-Z到Z的变换变回左手系。
为啥多次一举了,怀疑这个结论的正确性。下面做实验,用IMGizmos绘制坐标轴。代码如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
using UnityEngine;

namespace GYGame
{
/// <summary>
/// 出生点
/// </summary>
public class PlayerStart : MonoBehaviour
{
public float GizmosHeight { get; set; } = 2.0f;

void OnDrawGizmos()
{
IMGizmos.Line3D(transform.position, transform.position + transform.up * GizmosHeight, Color.green);
IMGizmos.Line3D(transform.position, transform.position + transform.right * GizmosHeight, Color.red);
IMGizmos.Line3D(transform.position, transform.position + transform.forward * GizmosHeight, Color.blue);
}
}
}

选中场景相机,可以得到下面结果,

可以看到右下角的场景相机画面里面有显示PlayerStart的Gizmos,Gizmos显示的坐标系是左手系,跟右上角显示的坐标系是一致的。同时,引擎自带的Gizmos显示的摄像机前向也是Z轴正向。
因此,推测我实验的Unity版本是2020,与UnityShader入门精要使用的Unity5.X版本,摄像机空间的旋向性已经发生了变化。

4.2 UnrealEngine


虚幻和Unity一样也是采用左手坐标系,不过其是Z轴朝上,Y轴朝外。沿着X轴旋转90度,可以得到Z轴朝内,Y轴朝上,那么和Unity的是一致的。
推测,其余的空间的坐标系旋向和Unity的是一致。摄像机空间的旋向也可以用类似4.1的方式绘制Gizmos,然后选中摄像机,查看摄像机的绘制结果。

五、参考资料

Shader入门精要
图形坐标系的跨平台适配