美文网首页
Metal中两种projection matrix的推导

Metal中两种projection matrix的推导

作者: chocoford | 来源:发表于2018-06-22 11:18 被阅读0次

    接触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
    

    相关文章

      网友评论

          本文标题:Metal中两种projection matrix的推导

          本文链接:https://www.haomeiwen.com/subject/gpghyftx.html