美文网首页
OpenGL 矩阵操作

OpenGL 矩阵操作

作者: 君幸食j | 来源:发表于2020-08-21 17:23 被阅读0次

    向量

    1.png

    (x,y,z) 既能表示坐标空间中的一个位置,还能表示一个向量。3个值 (x,y,z)组合起来表示两个重要的值,方向和数量。例如,x轴就是向量 (1,0,0),在x方向为+1,而在y方向和z方向则为0。

    math3d库有两个数据类型,能够表示⼀个三维或者四维向量。M3DVector3f可以表示⼀个三维向量(x,y,z),⽽M3DVector4f则可以表示⼀个四维向量(x,y,z,w)。在典型情况下,w坐标设为1.0。x,y,z值通过除以w来进⾏缩放。⽽除以1.0则本质上不改变x,y,z值。

    typedef float M3DVector3f[3];
    typedef float M3DVector4f[4];
    
    //声明一个三分量向量
    M3DVector3f vVector;
    
    //声明并初始化一个四分量向量
    M3DVector4f vVertex = {0.0f,0.0f,1.0f,1.0f};
    
    //声明一个三分量顶点数组,例如生成一个三角形
    M3DVector3f vVerts[] = {-0.5f,0.0f,0.0f,                  
                             0.5f,0.0f,0.0f,                                                     
                             0.0f,0.5f,0.0f};
    

    点乘 2.png

    • 实现点乘⽅法:点乘运算返回2个向量之间的夹⻆
    • ⽅法1:返回的是-1,1之间的值。它代表这个2个向量的余弦值。
    float m3dDotProduct3(const M3DVector3f u,const M3DVector3f v);
    
    • ⽅法2:返回2个向量之间的弧度值。
    float m3dGetAngleBetweenVector3(const M3DVector3f u,const M3DVector3f v);
    

    叉乘

    3.png

    叉乘运算结果返回⼀个新的向量,这个新的向量与原来的2个向量垂直

    void m3dCrossProduct3(M3DVector3f result,const M3DVector3f u ,const M3DVector3f v);
    

    矩阵

    矩阵是一组排列在统一的行和列中的数字,用程序设计语言来说就一个二维数组,矩阵中的每一行(或每一列)的元素都必须和其他行(或列)的元素数相同。 4.png

    矩阵之间可以进行乘法和加法,也能与向量或者标量相乘。标量就是一个普通的单独数据,用来表示大小和特定量。

    typedef float M3DMatrix33f[9];
    typedef float M3DMatrix44f[16];
    

    理解变换

    把3D数据“压扁”成2D数据的处理过程叫做投影,投影中出现的透视变换就是OpenGL中的一种变换。变换还允许我们旋转、移动、伸展、收缩或扭曲对象。一般渲染一个3D图形,会发生3种类型的几何变换。

    变换 应用
    视图 指定观察者或照相机的位置
    模型 在场景中移动物体
    模型视图 描述视图和模型变换的二元性
    投影 改变视景体的大小和设置它的投影方式
    视口 这是一种伪变换,只是对窗口上的最终输出进行缩放

    视觉坐标

    视觉坐标是一个虚拟的固定坐标系,通常作为参考坐标系使用。视觉坐标是相对于观察者的视角而言的,可以视为“绝对的”屏幕坐标。 5.png

    视图变换

    视图变换允许我们把观察点放在任何位置,并允许在任何方向上观察场景。确定视图变换就像在场景中放置照相机并让它指向某个方向。在应用其他模型变换前,必须先应用视图变换。

    模型变换

    模型变换用于操纵模型和其中的特定对象,平移、旋转或缩放对象。


    6.png
    7.png

    模型视图的二元性

    将对象向后移动和将参考坐标系向前移动在视觉上没有区别,视图变换和模型变换一般是结合在一起的。把这两种变换在变换管线中进行组合,成为一个单独的矩阵,即模型视图矩阵。 8.png

    投影变换

    投影变换将在模型视图变换之后应用到顶点上。这种投影实际上定义了视景体并创建了裁剪平面。分为正投影(或平行投影)和透视投影,正投影大多绘制2D图形,透视投影都应用在3D图形。 9.png

    视口变换

    把一个场景的二维投影映射到物理窗口称为视口变换,视口变换会将”规范化“设备坐标重新映射到窗口坐标上。

    模型视图矩阵

    模型视图矩阵是一个 4 x 4 矩阵,它表示一个变换后的坐标系,我们可以用来放置对象和确定对象的方向。可以将单个顶点数据乘以模型视图矩阵得到新的视觉坐标。 10.png

    矩阵分为行矩阵和列矩阵。

    • 转置矩阵:将⾏矩阵A的换成同序列列得到的矩阵,叫做A的转换矩阵。计为AT。

    • 矩阵转置,其实就是⾏列互换。有很多地⽅都⽤到。⽐如数学、程序语⾔、计算机数据结构中 11.png
    • ⼀个4*4矩阵是如何在3D空间中表示⼀个位置和⽅向的

    • 列向量进⾏了特别的标注:矩阵的最后⼀⾏都为0,只有最后⼀个元素为1 12.png

    平移

    平移矩阵将顶点沿着3个坐标轴中的一个或多个进行平移。 13.png

    在math3d库调用函数如下:

    void m3dTranslationMatrix44(M3DMatrix44f m, float x, float y, float z);
    

    旋转

    旋转矩阵将一个对象沿着3个坐标轴中的一个或者任意向量进行旋转。 14.png

    在math3d库调用函数如下:

    m3dRotationMatrix44(M3DMatrix44f m, float angle, float x, float y, float z);
    

    缩放

    缩放矩阵沿着3个坐标轴方向按照指定因子放大或缩小所有顶点,以改变对象的大小。 15.png

    在math3d库调用函数如下:

    void m3dScaleMatrix44(M3DMatrix44f m, float xScale, float yScale, float zScale);
    

    综合变换

    将以上变换结合,可以在math3d库调用以下函数将两个矩阵相乘。需要注意的是,矩阵相乘是有顺序的。例如,用一个旋转矩阵乘以一个平移矩阵,与用一个平移矩阵乘以一个旋转矩阵是不同的。

    void m3dMatrixMultiply44(M3DMatrix44f product, const M3DMatrix44f a, const M3DMatrix44f b);
    

    变换管线

    16.png

    首先,顶点将被视为一个 1x4 矩阵,其中前3个值为x,y和z坐标。第4个数字是一个缩放因子,可以手动进行设置。这就是w坐标,默认为1.0,一般很少去直接修改这个值。然后,顶点将乘以模型视图矩阵,生成变换的视觉坐标。随后,视觉坐标再乘以投影矩阵,生成裁剪坐标。裁剪坐标位于+/-1.0单位坐标系内。将有效地将所有位于这个裁剪空间之外的数据消除掉。裁剪坐标随后再除以w坐标,生成规范化的设备坐标。其中w值可能会被投影矩阵或模型视图矩阵修改,这取决于所发生的变换。透视除法将作为图元装配过程的一部分进行。最后,坐标三元组将通过视口变换被映射到2D平面上。这项操作也是由一个矩阵来表示的,但不能直接指定或者修改这个矩阵。OpenGL 将在内部根据指定的 glViewport 值来设置这个矩阵。

    使⽤矩阵堆栈

    //类型
    GLMatrixStack::GLMatrixStack(int iStackDepth = 64);
    
    //在堆栈顶部载⼊⼀个单元矩阵
    void GLMatrixStack::LoadIdentity(void);
    
    //在堆栈顶部载⼊任何矩阵
    //参数:4*4矩阵
    void GLMatrixStack::LoadMatrix(const M3DMatrix44f m);
    
    //矩阵乘以矩阵堆栈顶部矩阵,相乘结果存储到堆栈的顶部
    void GLMatrixStack::MultMatrix(const M3DMatrix44f);
    
    //获取矩阵堆栈顶部的值 GetMatrix 函数
    //为了适应GLShaderMananger的使⽤,或者获取顶部矩阵的副本
    const M3DMatrix44f & GLMatrixStack::GetMatrix(void);
    void GLMatrixStack::GetMatrix(M3DMatrix44f mMatrix);
    

    压栈与出栈

    //将当前矩阵压⼊堆栈(栈顶矩阵copy ⼀份到栈顶)
    void GLMatrixStack::PushMatrix(void);
    
    //将M3DMatrix44f 矩阵对象压⼊当前矩阵堆栈
    void PushMatrix(const M3DMatrix44f mMatrix);
    
    //将GLFame 对象压⼊矩阵对象
    void PushMatrix(GLFame &frame);
    
    //出栈(出栈指的是移除顶部的矩阵对象)
    void GLMatrixStack::PopMatrix(void);
    
    矩阵压栈出栈流程.png

    仿射变换

    GLMatriStack类也内建了对创建旋转、平移和缩放矩阵的支持。

    //Rotate 函数angle参数是传递的度数,⽽不是弧度
    void MatrixStack::Rotate(GLfloat angle,GLfloat x,GLfloat y,GLfloat z);
    
    void MatrixStack::Translate(GLfloat x,GLfloat y,GLfloat z);
    
    void MatrixStack::Scale(GLfloat x,GLfloat y,GLfloat z);
    

    使⽤照相机(摄像机) 和 ⻆⾊帧 进⾏移动

    class GLFrame 
    { 
     protected: 
     M3DVector3f vOrigin; // Where am I? 
     M3DVector3f vForward; // Where am I going? 
     M3DVector3f vUp; // Which way is up?
    } 
    
    //将堆栈的顶部压⼊任何矩阵
    void GLMatrixStack::LoadMatrix(GLFrame &frame); 
    
    //矩阵乘以矩阵堆栈顶部的矩阵。相乘结果存储在堆栈的顶部
    void GLMatrixStack::MultMatrix(GLFrame &frame); 
    
    //将当前的矩阵压栈
    void GLMatrixStack::PushMatrix(GLFrame &frame); 
    

    相关文章

      网友评论

          本文标题:OpenGL 矩阵操作

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