OpenGL6-矩阵变换

314 阅读3分钟
01、数学概念:点、向量、矩阵
  • 计算机图形学中使用了大量的数学知识,尤其是矩阵和线性代数,3D图形编程虽说是先进的技术领域,但实现原理却可以追溯上百年;特别是3D效果的实现,比如移动、缩放、透视、纹理、光照、阴影等,很大程度上都以数学公式实现
点:
  • 之前有说过OpenGL的坐标采用的是右手坐标系(Direct3D是左手坐标系),在坐标系中表示一个点如(2,3,-4),或者使用齐次坐标(2,3,-4,1)表示,在GLSL代码中,采用vec3或vec4来表示一个点或者向量
矩阵:
  • 矩阵是矩形的值的阵列,具有行号和列号,3D图形计算中多使用4x4的矩阵,GLSL和GLM中采用mat4这个类来实例化并存储4x4矩阵

  • 图形学中多使用到矩阵的加法和乘法

  • 矩阵相乘:是前面矩阵的每一行乘于后面矩阵的每一列中的元素

点与矩阵相乘:
  • 通常将点作为列向量,并从右向左计算,这块在计算时,需要特别注意矩阵等式前面,这样相乘结果才能得到一个点坐标

02、采用矩阵实现三角形的偏移
  • 上一章我们在GLSL中创建了一个uniform vec4类型的变量,通过对每个顶点都加上这个变量来实现,除了这个,还可以使用矩阵相乘的方式进行实现
  • 在GLSL代码中新建uniform mat4矩阵变量,当每个顶点数据传入顶点着色器程序中,都使用这个矩阵变量程序坐标点,其结果就得到偏移后的点坐标
   const char *vertex_shader =
   "#version 410          \n"
   "layout(location=0) in vec3 a_Position;  \n"
   "uniform mat4 u_TransMatirx; \n"
   " void main() {"
   "  gl_Position = u_TransMatirx * vec4(a_Position, 1.0); \n"
   "}";

解析:

  • 假设要将点(0.0, 0.5, 0.0, 1.0)往右上角方向移动,即X轴加0.5距离,Y轴加0.5,Z轴不变,得到最终结果(0.5, 1.0, 0.0, 1.0),使用矩阵相乘等式表示如下:

  • 同样三角形的其他两个点也可以使用这个矩阵变量完成点的偏移,实现代码
    GLuint u_TransMatirx = glGetUniformLocation(renderProgram,"u_TransMatirx");
    glm::mat4 trans = glm::mat4(
                               1.0, 0.0, 0.0, 0.0,
                               0.0, 1.0, 0.0, 0.0,
                               0.0, 0.0, 1.0, 0.0,
                               0.5, 0.5, 0.0, 1.0
   );
   glUniformMatrix4fv(u_TransMatirx, 1, GL_FALSE, glm::value_ptr(trans));
  • 最终实现效果和之前一样

03、按列主序存储
  • 细心的读者会发现手写稿中矩阵的第一行第四列值是0.5,但是代码中是0.0,但是第四行第一列的值却又是0.5
  • 这是因为通过数组来存储矩阵的每个元素,其元素是按照列主序来存储在数组中的

GLM库
  • 在C++代码中,我们使用了一个新的GLM库,可以通过brew install glm下载安装并引用
04、矩阵方式旋转
  • 旋转矩阵,沿Z轴旋转90度
   const char *vertex_shader =
   "#version 410          \n"
   "layout(location=0) in vec3 a_Position;  \n"
   "uniform mat4 u_RotateMatirx; \n"
   " void main() {"
   "  gl_Position = u_RotateMatirx * vec4(a_Position, 1.0); \n"
   "}";
   
    GLuint u_RotateMatirx = glGetUniformLocation(renderProgram,"u_RotateMatirx");   
   float angle = 90;
   double radia = M_PI * angle / 180.0;
   float cosB = cos(radia);
   float sinB = sin(radia);
   // 创建旋转矩阵
    glm::mat4 rotate = glm::mat4(
                                cosB, sinB, 0.0, 0.0,
                               -sinB, cosB, 0.0, 0.0,
                                 0.0,  0.0, 1.0, 0.0,
                                 0.0,  0.0, 0.0, 1.0
   );
   glUniformMatrix4fv(u_RotateMatirx, 1, GL_FALSE, glm::value_ptr(rotate));

  • 绕其他轴旋转的矩阵公式:

05、缩放矩阵
   const char *vertex_shader =
   "#version 410          \n"
   "layout(location=0) in vec3 a_Position;  \n"
   "uniform mat4 u_ScaleMatirx; \n"
   " void main() {"
   "  gl_Position = u_ScaleMatirx * vec4(a_Position, 1.0); \n"
   "}";
  
    GLuint u_ScaleMatirx = glGetUniformLocation(renderProgram,"u_ScaleMatirx");
    glm::mat4 scale = glm::mat4(
                                 0.5,  0.0, 0.0, 0.0,
                                 0.0,  0.5, 0.0, 0.0,
                                 0.0,  0.0, 1.0, 0.0,
                                 0.0,  0.0, 0.0, 1.0
   );
   glUniformMatrix4fv(u_ScaleMatirx, 1, GL_FALSE, glm::value_ptr(scale));
  • 效果图

07、调用glm库封装函数
  • 上面的三角形操作都是我们自己创建并编写矩阵的具体元素,其实glm提供了对应的封装方法,可直接调用实现上面的效果
平移操作:
    glm::mat4 trans = glm::mat4(1.0f);
    trans = glm::translate(trans, glm::vec3(0.5f, 0.5f, 0.0f));

旋转操作:
    glm::mat4 rotate = glm::mat4(1.0f);
    rotate = glm::rotate(rotate,glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f));

缩放操作:
    glm::mat4 scale = glm::mat4(1.0f);
    scale = glm::scale(scale, glm::vec3(0.5f, 0.5f, 0.0f));