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下载安装并引用
- 也可以通过官网或github下载:github.com/g-truc/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));