一、不同类型矩阵变换先后顺序
对 OpenGL 中的 模型视图矩阵进行 缩放 , 旋转 , 平移 操作时 , 先旋转再移动 , 与先移动再旋转 的效果是不同的 ;
矩阵具有叠加性 , 先移动再旋转 , 与先旋转再移动 , 最终的模型视图矩阵的值是不同的 ;
举个例子 : 人走路时 , "先向左转 , 然后再走 100 米 " , 与 " 先走 100 米 , 再向左转 " 达到的目的地肯定是不同的 ;
先旋转后移动代码 :
// 渲染场景
// 设置单位矩阵
glLoadIdentity();
// 矩阵缩放
// 缩放的是下面设置的点的坐标
// 每个参数都影响 x , y , z 分量
//glScalef(2.0f, 2.0f, 1.0f);
// 矩阵旋转
// glRotatef (GLfloat angle, GLfloat x, GLfloat y, GLfloat z);
// 第 1 个参数是旋转角度 , 后面三个参数的值代表是否绕该轴旋转 ,
// 如果对应值设置为 1 , 则绕该轴旋转
// 这里设置的是绕 z 轴旋转 30 度
glRotatef(30.0f, 0.0f, 0.0f, 1.0f);
// 平移变换
// 设置 xyz 三个方向平移的值
glTranslatef(0.0f, -2.0f, 0.0f);
效果 :
先移动后旋转代码 :
// 设置单位矩阵
glLoadIdentity();
// 矩阵缩放
// 缩放的是下面设置的点的坐标
// 每个参数都影响 x , y , z 分量
//glScalef(2.0f, 2.0f, 1.0f);
// 平移变换
// 设置 xyz 三个方向平移的值
glTranslatef(0.0f, -2.0f, 0.0f);
// 矩阵旋转
// glRotatef (GLfloat angle, GLfloat x, GLfloat y, GLfloat z);
// 第 1 个参数是旋转角度 , 后面三个参数的值代表是否绕该轴旋转 ,
// 如果对应值设置为 1 , 则绕该轴旋转
// 这里设置的是绕 z 轴旋转 30 度
glRotatef(30.0f, 0.0f, 0.0f, 1.0f);
效果 :
二、渲染前不设置单位阵
由上面的示例可知 , 矩阵的运算具有叠加性 , 每次矩阵操作前 , 都应该恢复成单位阵后 , 再进行操作 , 否则每次绘制的结果都不一样 ;
如果将 设置单位矩阵 的代码注释掉 , 此时三角形就会到处乱窜 ,
// 设置单位矩阵
//glLoadIdentity();
每次的矩阵操作都叠加上一次的矩阵操作 , 最终的结果不可预知 ;
这里除了设置单位阵之外 , 还可以使用压栈和出栈操作 ;
矩阵压栈 :
// 矩阵压栈
glPushMatrix();
矩阵出栈 :
// 矩阵出栈
glPopMatrix();
三、矩阵的压栈和出栈原理分析
这里引入两个重要的操作 : 矩阵的 压栈 和 出栈 , 这是 OpenGL 固定管线中的重要操作 ;
显卡栈 : 矩阵在显卡中有一个栈 , 该显卡中的栈可以存储 n n n 个矩阵 ;
栈顶矩阵 : 栈顶的矩阵是 模型视图 ( ModelView ) 矩阵 , 该矩阵的值是 单位阵 E \rm E E ( 主对角线元素是 1 1 1 , 其它位置都是 0 0 0 ) ;
矩阵压栈 : 调用 GLPushMatrix 方法进行压栈时 , 会将栈顶的矩阵拷贝一份 , 然后放在栈顶 , 原来的模型视图矩阵 , 就处于栈顶的下方位置 , 即栈内的第二个元素 ;
矩阵压栈后 , 所有的对于矩阵的操作都是针对栈顶的 矩阵 进行的操作 , 该栈顶矩阵是 模型视图矩阵 的一份拷贝 ;
矩阵出栈 : 调用 方法进行出栈操作 , 就将已经修改过的 模型视图矩阵 的拷贝弹出栈 , 恢复成原始的 模型视图矩阵 , 此时的模型视图矩阵就是原本的矩阵 , 不再需要在渲染开始的位置 , 设置单位阵了 ;
矩阵的 压栈 出栈 可以嵌套多层 , 用于绘制复杂的联动模型 ;
嵌套两层的 压栈 出栈 操作 :
第一次压栈 : 将 E \rm E E 拷贝一份 , A \rm A A 矩阵 , 放在栈顶 , 对 A \rm A A 矩阵进行一系列操作 ;
第二次压栈 : 将 A \rm A A 矩阵拷贝一份 , B \rm B B 矩阵 , 放在栈顶 , 对 B \rm B B 矩阵进行一系列操作 ;
第一次出栈 : 将 B \rm B B 矩阵弹出 , 即从栈顶移出 , 恢复成 A \rm A A 矩阵 ;
第二次出栈 : 将 A \rm A A 矩阵弹出栈 , 恢复成单位阵 E \rm E E ;
四、矩阵的压栈和出栈代码示例
矩阵的压栈和出栈代码示例 :
// 只显示正面 , 不显示背面
//glEnable(GL_CULL_FACE);
// 设置顺时针方向 CW : Clock Wind 顺时针方向
// 默认是 GL_CCW : Counter Clock Wind 逆时针方向
//glFrontFace(GL_CW);
// 默认模式, 填充模式 , 如果不设置就默认为填充模式
//glPolygonMode(GL_FRONT, GL_FILL);
// 设置线框模式
// 设置了该模式后 , 之后的所有图形都会变成线
//glPolygonMode(GL_FRONT, GL_LINE);
// 设置点模式
// 设置了该模式后 , 之后的所有图形都会变成点
//glPolygonMode(GL_FRONT, GL_POINT);
// 将方形的点变为圆点
//glEnable(GL_POINT_SMOOTH);
//glEnable(GL_BLEND);
// 主消息循环:
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// 渲染场景
// 设置单位矩阵
//glLoadIdentity();
// 矩阵压栈
glPushMatrix();
// 矩阵缩放
// 缩放的是下面设置的点的坐标
// 每个参数都影响 x , y , z 分量
//glScalef(2.0f, 2.0f, 1.0f);
// 平移变换
// 设置 xyz 三个方向平移的值
glTranslatef(0.0f, -2.0f, 0.0f);
// 矩阵旋转
// glRotatef (GLfloat angle, GLfloat x, GLfloat y, GLfloat z);
// 第 1 个参数是旋转角度 , 后面三个参数的值代表是否绕该轴旋转 ,
// 如果对应值设置为 1 , 则绕该轴旋转
// 这里设置的是绕 z 轴旋转 30 度
glRotatef(30.0f, 0.0f, 0.0f, 1.0f);
// 清除缓冲区 ,
// 使用之前设置的 glClearColor(1.0, 0.0, 0.0, 1.0) 擦除颜色缓冲区
// 红色背景
glClear(GL_COLOR_BUFFER_BIT);
// 设置当前的绘制颜色 , 4 个 unsigned byte
// 每个颜色的分量占一个字节
// 参数数据是 R 红色 G 绿色 B 蓝色 A 透明度
// 下面设置的含义是白色, 绘制点的时候, 每次都使用白色绘制
glColor4ub(255, 255, 255, 255);
// 设置当前点的大小
glPointSize(5.0f);
// 设置线的宽度
glLineWidth(5.0f);
//glBegin(GL_POINTS); // 绘制点
//glBegin(GL_LINES); // 绘制线
//glBegin(GL_LINE_STRIP);// 绘制前后连接的点组成的线
//glBegin(GL_LINE_LOOP); // 绘制前后连接的点组成的线 , 并且收尾相连
//glBegin(GL_TRIANGLES); // 绘制多个三角形
//glBegin(GL_TRIANGLE_STRIP); // 绘制 GL_TRIANGLE_STRIP 三角形
//glBegin(GL_TRIANGLE_FAN); // 绘制三角形扇
// 绘制三角形
glBegin(GL_TRIANGLES);
// 1. 设置白色 , glVertex3f (GLfloat x, GLfloat y, GLfloat z)
glColor4ub(255, 255, 255, 255);
glVertex3f(0.0f, 1.0f, -5.0f);
// 2. 设置绿色
glColor4ub(0, 255, 0, 255);
glVertex3f(-1.0f, 0.0f, -5.0f);
// 3. 设置蓝色
glColor4ub(0, 0, 255, 255);
glVertex3f(1.0f, 0.0f, -5.0f);
// 绘制三角形结束
glEnd();
// 矩阵出栈
glPopMatrix();
// 将后缓冲区绘制到前台
SwapBuffers(dc);
}
最终效果 :
五、相关资源
GitHub 地址 : https://github.com/han1202012/OpenGL
( GitHub 源码始终都会随着后续博客的进度更新覆盖 , 可能没有本博客的相关源码 , 推荐下载博客源码快照 ) ;
博客源码快照 : https://download.csdn.net/download/han1202012/14901367
( 该源码是 Windows 桌面程序 , 使用 Visual Studio 2019 打开 )