OpenGL的矩阵

作者: 黑眼豆豆_ | 来源:发表于2020-07-19 22:45 被阅读0次

    假设,在空间中有一个点,使用XYZ描述它的位置。此时让它进行旋转或者平移,我们需要知道这个点的新位置,那么此时,我们就可以用矩阵进行计算得出新点的位置。

    定义

    矩阵(Matrix)是一个按照长方阵列排列的复数实数集合 [1] ,最早来自于方程组系数常数所构成的方阵

    表达式

    假如我们定义一个n行m列的矩阵A,那么我们可以用如下的公式进行表达:


    矩阵.jpg

    上图就是一个n行m列的矩阵,其中a为矩阵中的元素,a的第一个下标表示所处行的位置,第二个下标表示所处列的位置。

    矩阵只有一行或者一列的时候,这个矩阵可以称之为向量。

    在OpenGL中,矩阵可以进行以下定义:

    //3*3的矩阵
    M3DMatrix33f transform
    //4*4的矩阵
    M3DMatrix44f transform
    

    矩阵的运算

    • 加法
      矩阵加法就是矩阵里对应的元素进行加法,公式如下:

      矩阵加法.jpg 矩阵的加法满足下列运算律:
      A+B = B+A
      (A+B)+C = A+(B+C)
      矩阵加法必须是同型矩阵,即A,B矩阵行列相同
    • 数乘
      矩阵的数乘等于数乘以矩阵里的每一个元素,公式如下:

      矩阵数乘.jpg
    • 转置
      把矩阵A的行和列互相交换所产生的矩阵称为A的转置矩阵 ,这一过程称为矩阵的转置。公式如下:

      矩阵转置.jpg
    • 矩阵相乘
      矩阵相乘公式如下:

      矩阵相乘.jpg 注意:两个矩阵的乘法仅当第一个矩阵A的列数和另一个矩阵B的行数相等时才能定义。例如,当A矩阵为mn矩阵时,B矩阵必须为nx才能进行矩阵相乘,切得到的结果为m**x。
      故矩阵相乘不满足交换律。

    单元矩阵

    单元矩阵是一个对角线为非零元素,其它元素为零的方形矩阵。


    单元矩阵.jpg

    上图就是一个单元矩阵,对角线元素为1,其他元素为0。
    单元矩阵用m3d库定义方法:

    //3*3的单元矩阵(float)
    void m3dLoadIdentity33(M3DMatrix33f m);
    //3*3的单元矩阵(double)
    void m3dLoadIdentity33(M3DMatrix33d m);
    //4*4的单元矩阵(float)
    void m3dLoadIdentity44(M3DMatrix44f m);
    //4*4的单元矩阵(double)
    void m3dLoadIdentity44(M3DMatrix44d m);
    

    行优先矩阵和列优先矩阵

    行优先矩阵:指的是矩阵元素以行的顺序进行排列。
    列优先矩阵:指的是矩阵元素以列的顺序进行排列。

    行优先矩阵和列优先矩阵.jpg 在OpenGL中,我们一般使用列优先矩阵。

    OpenGL中的变化

    变换 解释
    视图变换 观察者的位置变化
    模型变换 在场景中物体的变换(平移、缩放、旋转等)
    模型视图变换 模型、视图变换相乘后的结果
    投影 改变视景体的大小和设置投影方式(正投影、透视投影)
    视口 对窗口进行缩放

    视图变换

    一般来说,视图变换是引用场景中的第一个变换,它用来确定观察者的位置,默认情况中,在透视投影下位于原点(0,0,0),并沿着Z轴往负方向观察。(如同从屏幕上往屏幕里面看)。
    为什么说,视图变换要放在最开始呢?
    这是因为对于坐标系而言,进行试图变换,就会更换坐标系,接下来的变换都会基于新的坐标系进行。(就像我们看电视,会想用一个更舒服、更好的姿势进行观看)

    模型变换

    模型变换指的是针对物体进行的一系列操作变换,例如平移、旋转、缩放等。

    • 平移
      平移顾名思义指的是一个物体大小、弧度不变,按照一个方向进行移动。 平移
      如图所示,该正方形沿着Z轴进行了平移,公式如下:
    //m是一个指针,存储了变换后的结果
    //x表示x轴移动的距离
    //y表示y轴移动的距离
    //z表示z轴移动的距离
    inline void m3dTranslationMatrix44(M3DMatrix44f m, float x, float y, float z)
    
    • 旋转
      *平移值得是一个物体 在某个点进行旋转
      image.png

    如图所示,该正方形在原点进行旋转,公式如下:

    //m是一个指针,存储了变换后的结果
    //angle表示旋转的角度
    //x表示x轴移动的距离
    //y表示y轴移动的距离
    //z表示z轴移动的距离
    void m3dRotationMatrix44(M3DMatrix44d m, double angle, double x, double y, double z);
    
    • 缩放
      缩放指的是一个物体按一定比例进行大小的变换。
      image.png 公式如下:
    //m是一个指针,存储了变换后的结果
    //x表示x轴缩放的比例
    //y表示y轴缩放的比例
    //z表示z轴缩放的比例
    inline void m3dScaleMatrix44(M3DMatrix44f m, float xScale, float yScale, float zScale)
    
    • 投影变换
      投影变换分为正投影和透视投影。 投影投影和正投影.jpg
      上图中,第一个投影是透视投影,模型距离屏幕越近时,物体看起来越大,越远时越小。
      第二个投影是正投影,不论物体的远近,大小看起来都是一样的。
    • 综合变换
      当我们需要对模型先进行平移火旋转时怎么处理呢?我们使用矩阵相乘。公式如下:
    //product存放矩阵相乘后的结果
    //a表示第一个矩阵
    //b表示第二个矩阵
    void m3dMatrixMultiply44(M3DMatrix44f product, const M3DMatrix44f a, const M3DMatrix44f b);
    

    矩阵堆栈

    在之前的用OpenGL绘制金字塔、圆环、扇形案例中,我们已经提到过矩阵堆栈。

    矩阵堆栈.png
    如上图所示,在栈的流程中,后来的矩阵都是通过push的方式往里面加入,而且新假如的矩阵都是存放在栈顶。当要离开栈时,需要用到pop方法,pop时是将栈顶的元素从栈中拿出来,所以栈遵循先进后出的规则(FILO)。

    用法

    • 矩阵定义
    //矩阵的深度为64
    GLMatrixStack(int iStackDepth = 64);
    
    • 单元矩阵堆栈
    //加载单元矩阵
    inline void LoadIdentity(void) {
        m3dLoadIdentity44(pStack[stackPointer]); 
    }
    

    正如我们所说的矩阵堆栈流程,加载单元矩阵其实就是往栈顶加载一个元素,但是并没有告诉我们是什么元素,那就是默认添加一个单元矩阵。

    • 添加矩阵
    //加载矩阵
    inline void LoadMatrix(const M3DMatrix44f mMatrix) { 
        m3dCopyMatrix44(pStack[stackPointer], mMatrix); 
    }
    

    加载矩阵就是往栈顶添加了一个元素。

    • 矩阵相乘
    inline void MultMatrix(const M3DMatrix44f mMatrix) {
          M3DMatrix44f mTemp;
          m3dCopyMatrix44(mTemp, pStack[stackPointer]);  
          m3dMatrixMultiply44(pStack[stackPointer], mTemp, mMatrix);
    }
    

    由代码可以看出,首先从栈顶复制一个矩阵存放在mTemp矩阵里,然后和传入的mMatrix相乘后存入栈顶。

    • 入栈(无参数)
    inline void PushMatrix(void) {
                if(stackPointer < stackDepth) {
                    stackPointer++;
                    m3dCopyMatrix44(pStack[stackPointer], pStack[stackPointer-1]);
                    }
                else
                    lastError = GLT_STACK_OVERFLOW;
                }
    

    可以看到,判断stackPointer是否大于栈的深度,如果小于,那么stackPointer加一,然后把栈里的第二个元素复制到栈顶

    • 入栈(有参数)
    void PushMatrix(const M3DMatrix44f mMatrix) {
                if(stackPointer < stackDepth) {
                    stackPointer++;
                    m3dCopyMatrix44(pStack[stackPointer], mMatrix);
                    }
                else
                    lastError = GLT_STACK_OVERFLOW;
                }
    

    可以看到,判断stackPointer是否大于栈的深度,如果小于,那么stackPointer加一,然后把传入的参数放到栈顶

    • 出栈
    inline void PopMatrix(void) {
                if(stackPointer > 0)
                    stackPointer--;
                else
                    lastError = GLT_STACK_UNDERFLOW;
                }
    

    把stackPointer--从而移除最顶上的元素。

    相关文章

      网友评论

        本文标题:OpenGL的矩阵

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