zoukankan      html  css  js  c++  java
  • 统一D3D与OpenGL坐标系统

    作者:游蓝海(http://blog.csdn.net/you_lan_hai


            DirectX 3D与OpenGL坐标系统的差异性,给我们带来很大的麻烦,让跨平台编程的新手很困惑。最近在做一个跨平台的游戏,仔细看了下两者的矩阵,发现并没有什么大区别,将d3d左手系的矩阵传递给opengl shader完全可以正常工作。

            先说一下两者一些概念上的区别:

            (1)坐标系统不同

            d3d左手坐标系,opengl右手坐标系

            (2)矩阵行序不同

            d3d行优先,opengl列优先。这两个不同,直接导致了坐标变换顺序与矩阵乘法顺序的相反性。如果是先缩放,再旋转,最后平移,对应的矩阵分别为S、R、T,则d3d的最终矩阵为M = S * R * T,opengl为M = T * R * S

            (3)裁减空间z取值范围不同

            d3d是[0, 1],opengl是[-1, 1]


            表面上来看,两者矩阵差别很大,但其实不然。

    1.左右手坐标系

            对于显卡设备来说,设备坐标系是左手坐标系,即z轴指向屏幕里面,z值越大表示距离视线越远。因此,opengl的右手系,在进入裁减空间的时候,会转换成左手系。这也就是说,在渲染管线内部,坐标系是统一的。不管是左手坐标系矩阵,还是右手坐标系矩阵,只要变换到裁减空间中的点是左手系就可以了。


    2.矩阵行序

            行矩阵和列矩阵,在逻辑上一个是另一个的转置,但在物理存贮结构上却是完全一致的。如一个平移变换(x, y, z):


            需要注意的是,矩阵乘法并不关心矩阵是行矩阵还是列矩阵,都是按照第一个矩阵的行去乘以第二个矩阵的列。对于列矩阵而言,这正是其蹩脚的地方,为了保证乘法意义的有效性,其坐标变换顺序跟矩阵乘法顺序恰好相反。

            还要注意一点,在shader中,opengl的矩阵乘法规则跟d3d是不同的。按照矩阵乘法规则(第一个矩阵行*第二个矩阵列):

            d3d矩阵乘法:      Ma(0 1 2 3) * Mb(0 4 8 12)

            opengl矩阵乘法: Ma(0 4 8 12) * Mb(0 1 2 3)

            因此,对于opengl shader而言,变换顺序跟矩阵乘法顺序依然是反的。如果我们能将传入opengl shader的矩阵做一次转置,那么opengl shader的矩阵乘法意义将跟d3d shader完全一致!


    3.修改投影矩阵

            由于opengl的裁减空间z取值范围为[-1, 1]跟d3d的[0, 1]不同,我们不能简单的使用d3d投影矩阵,必须重新定义d3d投影矩阵。

    void Matrix::perspectiveProjectionLH2( float fov, float aspectRatio,
            float nearPlane, float farPlane )
    {
            float h = (1.0f / tanf(fov * 0.5f));
            float w = h / aspectRatio;
    
            float a = (farPlane + nearPlane) / (farPlane - nearPlane);
            float b = -2.0f * farPlane * nearPlane / (farPlane - nearPlane);
            
            m[0][0] = w; m[0][1] = 0; m[0][2] = 0; m[0][3] = 0;
            m[1][0] = 0; m[1][1] = h; m[1][2] = 0; m[1][3] = 0;
            m[2][0] = 0; m[2][1] = 0; m[2][2] = a; m[2][3] = 1;
            m[3][0] = 0; m[3][1] = 0; m[3][2] = b; m[3][3] = 0;
    }


    4.总结

            使用左手系变换来统一两个平台是比较方便的。总结一下修改opengl渲染管线的步骤:

            (1)在c++层统一使用左手坐标系变换;

            (2)修改投影矩阵,以适应裁减空间z坐标范围[-1, 1];

            (3)矩阵在传入shader的时候,将矩阵的转置矩阵传入;

            (4)在shader层,统一使用左手坐标系变换。

    如果,不想修改shader中的变换,只用做到(1)和(2)就够了。


    5.更多阅读

    推导投影矩阵 http://blog.csdn.net/popy007/article/details/4091967

    跨越opengl和d3d的鸿沟 http://www.cppblog.com/topjackhjj/articles/157038.html


  • 相关阅读:
    C++ primer 简读
    C++函数及类方法(待补充)
    C++参数传递-复制和引用
    c++动态分配内存
    C++内存与指针
    c++循环
    C++容器vector及迭代器对象iterator
    c++字符类型一些知识点
    CNN中卷积运算转化成矩阵乘法的实现——img2col+GEMM大矩阵乘法
    046 LeetCode go
  • 原文地址:https://www.cnblogs.com/ygxsk/p/7693987.html
Copyright © 2011-2022 走看看