美文网首页
03 - OpenGL ES学习之变换矩阵

03 - OpenGL ES学习之变换矩阵

作者: CoderP1 | 来源:发表于2021-11-30 15:05 被阅读0次

    在写这篇文章之前,我们先掌握一个重要的知识:矩阵。矩阵是一个按照长方阵排列的实数或者复数集合。是数学里一个重要的概念,在图像处理中图像的仿射变换一般可以表示为一个仿射矩阵和一张原始图像相乘的形式。想对矩阵有更深入的了解的,可以百度一下相关资料。下面我们学习下矩阵的几种算法;

    1.矩阵的加法运算

    3阶矩阵加法运算

    从图中可以看出3X3矩阵就像是一个3X3的表格,每个单元格中填写一个数。它的加法就是把两个矩阵对应位置的元素加起来放在结果矩阵对应的位置上。矩阵的加法运算要求两边的矩阵必须行数和列数相等。

    2. 矩阵的减法运算

    截屏2021-11-26 下午4.25.14.png

    矩阵的减法运算和加法运算类似,也很好理解,同样的,矩阵的减法运算也要求两边的矩阵必须行数和列数相等。

    3. 矩阵的乘法运算

    截屏2021-11-26 下午4.32.56.png

    如图所示,计算的结果的第一行第一列的数等于,矩阵A的第一行乘以矩阵B的第一列,计算的结果相加,以此类推。

    矩阵的除法在这里先暂时不做介绍,有兴趣的同学可以去搜索一下线性代数的相关知识。

    变换矩阵
    介绍完了矩阵,那么什么是变换矩阵呢?在图形绘制过程中,有三种变换,分别是平移(translate),缩放(scale),旋转(rotate)。如我们所知,在三维空间中,我们用坐标(x,y,z)来表示一个点的空间位置,那么实现上面三种变换的过程需要几个分量呢?对应的平移变换需要(tx,tz,ty)三分量的向量,旋转需要(rx,ry,rz),缩放需要(sx,sy,sz)。在渲染的时候把这些变量附加到原始的位置数据上就可以实现变换了。这种方式虽然可行但不够好,尤其是在GPU上这种方式产生的运算负担远大于使用矩阵。

    我们接下来实现如下图的效果,来了解这三种变换的应用


    matrix-transform.gif

    1.平移矩阵
    假设空间中有一个点(1, 3, 7),经过大小为(2, 5, 6)的平移,最终会平移到(1+2, 3 + 5, 7 + 6)这个点, 也就是 (3,8,13) 的位置。使用矩阵计算如下图:


    截屏2021-11-30 上午11.10.07.png

    2.缩放矩阵
    假设空间中有一个点(2,4,6),我们对它进行(0.5,0.5,0.5) 的缩放,最终结果是
    (1,2, 3)。这其中的矩阵变换如下图:

    截屏2021-11-30 上午11.26.09.png

    缩放矩阵的三个缩放元素sx,sy,sz,分布在从左到右的对角线上,矩阵相乘后位置的x,y,z分别乘以了sx,sy,sz,从而实现了缩放。

    3.旋转矩阵
    对于三维旋转矩阵比较复杂,不过旋转矩阵有两个重要的参数 一个是 旋转的角度,绕什么轴旋转,这里就不进行推到了,有兴趣的可以百度一下相关资料
    GLKit提供了构建旋转矩阵的API如下:

    GLKMatrix4MakeRotation(<#float radians#>, <#float x#>, <#float y#>, <#float z#>)
    

    第一个参数radians 表示旋转的角度,后面的 x,y,z 构成一个三分量向量,用来表示绕什么轴旋转。

    下面我们看一下示例代码的重要步骤:
    1.首先获取一个变量,随时间变换,这样才有动画效果。代码如下

    - (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
        _elapsedTime += 0.02;
        float varyFactor = sin(self.elapsedTime);
    

    2.然后加载顶点数据

        static GLfloat triangleData[36] = {
                0.0f,   0.5f,  0.0f,  1.0f,  0.0f,  0.0f,
               -0.5f,   0.0f,  0.0f,  0.0f,  1.0f,  0.0f,
                0.5f,   0.0f,  0.0f,  0.0f,  0.0f,  1.0f,
                0.0f,  -0.5f,  0.0f,  1.0f,  0.0f,  0.0f,
               -0.5f,   0.0f,  0.0f,  0.0f,  1.0f,  0.0f,
                0.5f,   0.0f,  0.0f,  0.0f,  0.0f,  1.0f,
            };
        glClear(GL_COLOR_BUFFER_BIT);
    

    3.构建变换矩阵:

       GLKMatrix4 scaleMatrix = GLKMatrix4MakeScale(varyFactor, varyFactor, 1.0);
        
        GLKMatrix4 rotateMatrix = GLKMatrix4MakeRotation(varyFactor, 0.0, 0.0, 1.0);
        
        GLKMatrix4 translationMatrix = GLKMatrix4MakeTranslation(varyFactor, 0.0, 0.0);
        
        self.transformMatrix = GLKMatrix4Multiply(translationMatrix, rotateMatrix);
        self.transformMatrix = GLKMatrix4Multiply(self.transformMatrix, scaleMatrix);
    

    注意:这里相乘的顺序translateMatrix * rotateMatrix * scaleMatrix,实际应用的时候是,先缩放,再旋转,最后平移,如果先平移的话,动画效果就不太正常了,有兴趣的可以改变顺序试一下。

    4.绘制过程

     //加载统一变量
        GLuint uniformLocation = glGetUniformLocation(_esContext.program, "transform");
        glUniformMatrix4fv(uniformLocation, 1, GL_FALSE, self.transformMatrix.m);
    
        
        //gen Buffer and Bind
        if (_vertexBufferId == 0) {
            glGenBuffers(1, &_vertexBufferId);
            glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferId);
            glBufferData(GL_ARRAY_BUFFER, sizeof(triangleData), triangleData, GL_STATIC_DRAW);
        }
        
        int colorLocation = glGetAttribLocation(_esContext.program, "vcolor");
       
        glEnableVertexAttribArray(GLKVertexAttribPosition);
        glEnableVertexAttribArray(colorLocation);
        
        
        GLuint offset  = 3 * sizeof(float);
        
        //加载顶点属性数据
        glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), NULL);
        glVertexAttribPointer(colorLocation, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (const void *) offset);
        _esContext.drawFunc(&_esContext);
        
        glDisableVertexAttribArray(GLKVertexAttribPosition);
        glDisableVertexAttribArray(colorLocation);
    

    这里有一个注意点,我们看一下顶点着色器:

    //顶点着色器
        char vShaderStr[] =
           "#version 300 es                          \n"
           " in vec4 vPosition;  \n"
           " in vec4 vcolor;     \n"
           "uniform mat4 transform;                  \n"
           "out vec4 f_color;                        \n"
           "void main()                              \n"
           "{                                        \n"
           "   f_color = vcolor;                     \n"
           "   gl_Position = transform * vPosition ;  \n"
           "}                                        \n";
    

    这里是gl_position = transform * vPosition ; 你可能会疑问:上面讲述的矩阵变换例子中,不都是被变换的点在左边吗,应该是 vPosition * transform才对呢,这里我们加载transform这个统一变量的时候,用的这个API

    glUniformMatrix4fv(<#GLint location#>, <#GLsizei count#>, <#GLboolean transpose#>, <#const GLfloat *value#>)
    

    这里第三个参数 transpose,就是指示矩阵是否转置,我们传GL_FALSE的时候,着色器需要写transform * vPosition才能正确显示需要的效果,如果传的是GL_TRUE ,着色器需要写为vPosition * transform 才能正确显示需要的效果,可以参照这个公式: A * B = (AT * BT )T ,这里涉及到矩阵转置的知识 ,有兴趣的可以自己看一下相关知识思考一下。
    参考文章:学习OpenGL ES之变换矩阵

    相关文章

      网友评论

          本文标题:03 - OpenGL ES学习之变换矩阵

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