Li Jiaheng's blog
871 字
4 分钟
LearnOpenGL·一·5 变换
2024-04-23

这是对learnopengl的简单笔记。原教程网址:learnopengl。原教程同时涉及图形学的基本理论与opengl API,本文更多关注API,而简化甚至省略了背后的图形学原理性内容。

常见的变换有放缩、平移、旋转。齐次坐标下它们的矩阵都很好推,略。此外还有仿射变换、投影变换等。
旋转变化中,很容易想到3维空间的欧拉角表示。但无论如何怎样决定xyz旋转的顺序,都可能导致万向节死锁,比如x-y-z顺序,可能x转完后,y转的时候把z移动到了x的位置,导致丢失了一个自由度,这意味着有部分旋转无法用单一固定的顺序表示。解决方法是四元数

用GLM库#

只用把GLM中的glm文件夹放入项目的include文件夹(或者加入到项目的include路径)就能使用,它全是头文件。常用以下三个:

#include <glm/glm.hpp>  
#include <glm/gtc/matrix_transform.hpp>  
#include <glm/gtc/type_ptr.hpp>  

初始化一个矩阵,新版本初始化矩阵默认是0矩阵,老版本模式是单位矩阵。要添加命名空间。初始化一个单位矩阵如下:

//老版本直接初始化.  
glm::mat4 trans; //4*4 matrix  
//新版本初始化单位矩阵  
glm::mat4 trans = glm::mat4(1.0f); //maybe glm::mat4 trans(1.0f) is Okay too.  

用GLM融合多个变换矩阵(就是矩阵相乘)。
GLM生成某种变换的矩阵,本质上就是合成到一个已有的变换矩阵(如果没有,就单位矩阵)中。

glm::mat4 trans;  //must be a unit matrix  
//平移translate.  
trans = translate(trans,glm::vec3(1.0,1.0,0.0)); //第一个参数是要融合的矩阵,第二个是平移方向.  
//放缩scale  
trans = scale(trans,glm::vec3(0.5,0.5,0.1));  //第二个参数是将每个轴变为原来的多少倍.  
//旋转rotate  
trans = rotate(trans, glm::radiants(90.0),glm::vec3(0.0,0.0,0.1));  
//第二个参数是弧度制的旋转角度,用radiants转化角度为弧度制.  
//第三个参数是绕轴旋转的那个轴,这里是z轴。  

代码中GLM合成变换矩阵的顺序是从左往右的,也就是说如果用合成矩阵代码的顺序为A,B,C,等效与A*B*C。所以实际的顺序其实是C,B,A。代码顺序是从左往右的!
GLM和OpenGL采用列主序的内存布局(fotran),一般的C等语言都是行主序的。所以将矩阵传给着色器的矩阵,会有一个是否转置矩阵的选项,这就是问要不要把行主序转化为列主序!

着色器中的变换矩阵#

顶点的变换应该通过着色器在GPU中完成,而不是每次都在渲染循环内完成。通过着色器定义mat4的uniform完成。注意着色器语言当然有内建的矩阵数据类型。
比如,在着色器中将顶点用旋转矩阵旋转90度:

#version 330 core  
layout (location = 0) in vec3 aPos;  
layout (location = 1) in vec2 aTexCoord;  
  
out vec2 TexCoord;  
  
uniform mat4 transform; //glUniformMatrix4fv()设置这个uniform变换矩阵  
  
void main()  
{  
    gl_Position = transform * vec4(aPos, 1.0f);  
    TexCoord = vec2(aTexCoord.x, 1.0 - aTexCoord.y); //旋转90度的讨巧.  
}  

而在主循环中:

glm::mat4 trans;  
trans = rotate(trans,glm::radiants(90),glm::vec3(0.0, 0.0, 0.1));  
unsigned int transformLoc = getUniformLocation(ourShader.ID,"transform");  
glUniformMatrix4fv(transformLoc,1,GL_FALSE,(glm::value_ptr(trans)));  

最后一个函数,第一个是uniform位置编号,第二个是要传送几个矩阵,第三个是是否要转置,第四个是要传送的数据。注意用 glm::value_ptr()转化矩阵为OpenGL可以识别接受的格式。

LearnOpenGL·一·5 变换
https://namisntimpot.github.io/posts/cg/opengl/一5-变换/
作者
Li Jiaheng
发布于
2024-04-23
许可协议
CC BY-NC-SA 4.0