Metal中两种projection matrix的推导

2018-06-22  本文已影响0人  chocoford

接触Metal有一段时间了,但是官方文档中对一个点如何从空间坐标转化成NDC(即 normalized device coordinates)没有作详细的说明。虽然其中原理都和OpenGL、Direct3D大同小异,但是我们知道Metal中NDC和OpenGL还不太一样,而且苹果还少有提及这个,我只在2016年wwdc上看到过这一闪而过的介绍。。
所以,Metal's NDC 究竟是什么样子的。它是的x,y范围都是是[-1, 1],这和OpenGL一样,并且指向上和右。但是z轴范围是[0, 1],并且0在前面,1在后面(从摄象机的视角出发)。
由于NDC的不同,所以MVP变换中的Projection和我们熟悉的OpenGL中的就不一样了。
OpenGL中,透视变换和正交变换是这个样子的。

OpenGL中透视变换矩阵(图片来源:OpenGL Superbible 7th Edition) OpenGL中正交变换矩阵(图片来源:OpenGL Superbible 7th Edition)

正交变换

正交变换很好理解,就是把一个任意大小的矩形体(可以这么叫吧。。)变换成一个标准的、边长为2的、中心在原点的矩形体。可以把这个矩阵分解成两个矩阵相乘,左边是缩放矩阵,右边是变换矩阵(即平移)。简单的说,就是先把矩形体中心移动到坐标原点,在对其进行缩放。
因此,Metal中的正交变换矩阵也很容易就推导出来了,基本和OpenGL版的一样,就是对z轴的缩放和平移有点区别。

(
2.0 / (right - left),  0.0,                  0.0,                 (left + right) / (left - right),
0.0,                   2.0 / (top - bottom), 0.0,                 (top + bottom) / (bottom - top),
0.0,                   0.0,                  1.0 / (farZ - nearZ), -nearZ / (farZ - nearZ),
0.0,                   0.0,                  0.0,                  1.0
)

矩阵的样子长这样
简单点写可以写成和官方代码中的一样。

var m = matrix_float4x4()
    
m.columns.0.x = 2.0 / width
    
m.columns.1.y = 2.0 / height
    
m.columns.2.z = 1.0 / (zFar-zNear)
    
m.columns.3.z = -zNear / (zFar-zNear)
m.columns.3.w = 1.0
    
return m

透视变换

而透视变换矩阵就稍微复杂一点了。
原理就不在这多说明了,直观上很简单,http://www.opengl-tutorial.org/beginners-tutorials/tutorial-3-matrices/中有两幅很直观的图表达这个变换的过程。
所以,对于已经设定好的平头椎体区域(frustum),从平头椎体坐标变换成变换后的矩形体坐标应遵循下图公式

平头椎体区域坐标到矩形体坐标(图片来源:Mathematics for 3D Game Programming and Computer Graphics3rd)
原理很简单,就是相似三角形
原理的直观表达(图片来源:CRCFundamentals of Computer Graphics 4th Edition 2015)

而从任意大小的矩形体变换成标准视觉空间(View Volume)上面有讲过,于是就有了这样一堆公式


NDCx轴坐标 NDCy轴坐标

然后将上上上上幅图中公式带入,则有


现在有了x轴、y轴的。而对于z轴,这里又有点小情况。

注意在标准化设备坐标系中OpenGL实际上使用的是左手坐标系(投影矩阵交换了左右手)

所以要翻转z轴,-n对应-1, -f对应1。至于原因我也不太清楚为什么又是左手又是右手的,我猜大概是为了方便接下来的计算。
从x轴y轴的表达式中可以看出都含有一个分母为Pz,于是我们也尝试从z轴弄出同样形式,至于原因,是为了凑成P′= (−x′Pz,−y′Pz,−z′Pz,−Pz)表达P = (x′, y′, z′)。

于是就设方程 代入n、f
解得 于是 所以就可以得出透视矩阵

(以上图片均来自:Mathematics for 3D Game Programming and Computer Graphics 3rd)

这是OpenGL中的过程。

而对于Metal,由于用的是左手坐标系,所以n,f都在坐标正轴,即


换句话说,就是m.columns.2.w = 1
然后就是类似的推导,x、y轴一样,z轴上稍微做一点小改变,由于不需要翻转,但是需要一样的形式(即Pz出现在分母中),所以可列相同的方程解A、B
(
2n / (r - l), 0, (r+l)/r-l, 0,
0, 2n / (t - b), (t+b)/(t-b), 0,
0, 0, farZ / (farZ - nearZ), - nearZ  * farZ / (farZ - nearZ),
0, 0, 1, 0
)

对于只需要fov和aspectRatio的情况,为

    var m : matrix_float4x4 = matrix_float4x4()
    
    let f : Float = 1.0 / tan(FieldOfView / 2.0)
    
    m.columns.0.x = f / aspectRatio
    m.columns.1.y = f
    
    m.columns.2.z = zFar / (zFar - zNear)
    m.columns.2.w = 1.0
    
    m.columns.3.z = -(zNear*zFar)/(zFar-zNear)
    
    return m
上一篇下一篇

猜你喜欢

热点阅读