OpenGL的glPushMatrix和glPopMatrix矩阵栈顶操作函数

article/2025/10/28 19:14:47

在之前的博客中,我就说过后面会详细讲解这两个函数。今天让我们来认识下它们(glPushMatrix和glPopMatrix函数)。


OpenGL中图形绘制后,往往需要一系列的变换来达到用户的目的,而这种变换实现的原理是又通过矩阵进行操作的。Opengl中的变换一般包括视图变换模型变换投影变换等,在每次变换后,opengl将会呈现一种新的状态(这也就是我们为什么会成其为状态机)。

有时候在经过一些变换后我们想回到原来的状态,就像我们谈恋爱一样,换来换去还是感觉初恋好,怎么办?opengl就帮我们提供了两个函数:giPushMatrix()和glPopMatrix();

首先我们要知道,对于矩阵的操作都是对于矩阵栈的栈顶来操作的。当前矩阵即为矩阵栈的栈顶元素,而对当前矩阵进行平移、旋转等的变换操作也同样是对栈顶矩阵的修改。所以我们在变换之前调用giPushMatrix()的话,就会把当前状态压入第二层,不过此时栈顶的矩阵也与第二层的相同

当经过一系列的变换后,栈顶矩阵被修改,此时调用glPopMatrix()时,栈顶矩阵被弹出,且又会恢复为原来的状态。 函数的作用过程可以用下图描述,更为直观。

这里写图片描述
这里写图片描述

在opengl场景中一般存在多种矩阵变换操作,而控制这些操作的命令主要用到

glMatrixMode(GLenum mode);
作用:用于指定用哪个矩阵作为当前矩阵,mode用于指定哪一种矩阵栈是其后矩阵操作的目标。mode可取:
GL_MODELVIEW: 把其后的矩阵操作施加于造型视图矩阵栈。(默认)
GL_PROJECTION: 把其后的矩阵操作施加于投影矩阵栈。
GL_TEXTURE: 把其后的矩阵操作施加于纹理矩阵栈。
注意上述三种模式分别对应了三种矩阵栈。
所以在场景中存在多种矩阵变换时,glPushMatrix()和glPopMatrix()一般情况下也要结合glMatrixMode(GLenum mode)运用,系统才知道具体操作的是哪个矩阵栈。

注意:
摄像机矩阵和模型矩阵用的是同一个矩阵,就是GL_MODELVIEW (model是模型搜索矩阵,view是摄像机矩阵,GL_MODELVIEW里保存的是这两个矩阵的积)。所以选择GL_MODELVIEW之后直接用glTranslate,glRotate之类的就行。

其实摄像机和模型矩阵本质上是一回事(这也是为什么OpenGL把这两个矩阵放在一起保存的原因),因为比如把整个世界向y+方向移动10跟把摄像机向y-方向移动10是等价的。旋转也是一样。

虽然矩阵里可以保存任何变换,但按照OpenGL的概念,model和view矩阵里只能保存平移,旋转和缩放project矩阵里只能保存投影矩阵viewport矩阵里只能保存二维平移和缩放。这样来看把model和view放在一起是合理的。他们之间的区别纯粹是人为的。

示例:

  1. 绘制太阳

在坐标系中心(原点)绘制一个红色的太阳。首先设置为红色,绘制一个半径为10的球体。代码如下:

 glColor3ub(255, 0, 0); glutSolidSphere(10.0f, 15, 15);

这里写图片描述
2、绘制火星
火星在相对太阳偏移90个单位的距离:

glRotatef(saturnRotAngle, 0.0f, 1.0f, 1.0f);

设置火星的颜色:

glColor3ub(255, 200, 100);

绘制火星,假设火星的半径为5:

glutSolidSphere(5.0f, 15, 15);

由于后面我们还需要绘制地球,而地球的位置时相对于太阳的位置,所以不需要上面的位置偏移积累。所以绘制完火星后要返回上一步的位置,分别在绘制火星的代码前面与后面添加函数glPushMatrix()与glPopMatrix()。完整代码如下:

 glColor3ub(255, 200, 100);//入栈glPushMatrix();glTranslatef(90.0f, 0.0f, 0.0f);glutSolidSphere(5.0f, 15, 15);//出栈glPopMatrix();

3、绘制地球
以相同于绘制火星的原理绘制地球,代码如下:

glColor3ub(0, 0, 200);
//入栈
glPushMatrix();
//沿x轴平移60个单位
glTranslatef(60.0f, 0.0f, 0.0f);
//画球
glutSolidSphere(7.0f, 15, 15);
//出栈
glPopMatrix();

这里写图片描述

4、绘制月亮
由于月球是地球的卫星,绕着地球转,所以月球的位置时相对于地球的位置。所以绘制月球跟绘制地球的代码要被glPushMatrix()与glPopMatrix()函数包着:

//地球glColor3ub(0, 0, 200);//入栈glPushMatrix();//平移60个单位glTranslatef(60.0f, 0.0f, 0.0f);glutSolidSphere(7.0f, 15, 15);//月亮glColor3ub(88, 88, 88);//平移20个单位glTranslatef(20.0f, 0.0f, 0.0f);glutSolidSphere(2.0f, 15, 15);//出栈glPopMatrix();

这里写图片描述

5、让天体动起来
每次重新绘制时要给火星、地球一个旋转的角度,每次以相同方向一定的角速度旋转。同样的原理去实现月球绕地球转。

static float earthRotAngle = 0.0f;
static float saturnRotAngle = 0.0f;
static float moonRotAngle = 0.0f;
//当旋转角度累加到大于360度时,控制角度0moonRotAngle += 15.0f;if(moonRotAngle > 360.0f)moonRotAngle -= 360.0f;glutSolidSphere(2.0f, 15, 15);//出栈glPopMatrix();earthRotAngle += 10.0f;if(earthRotAngle > 360.0f)earthRotAngle -= 360.0f;saturnRotAngle += 6.0f;if(saturnRotAngle > 360.0f)saturnRotAngle -= 360.0f;
//注册一个回调函数,定时刷新屏幕:
void TimerFunc(int value)
{
//重绘函数
glutPostRedisplay();
//定时器
glutTimerFunc(100, TimerFunc, 1);
}

这里写图片描述

总结:学了数据结构后,入栈出栈比较易懂,把各种矩阵就当作数据,无论栈顶的元素怎么改变,我把上一个压入的元素弹出后,那个元素没有改变,所以还是恢复到原来的样子。

完整代码:

#include <windows.h>
#include <gl/glut.h>
#include<math.h>const GLfloat PI = 3.1415f;
GLfloat xRot = 0.0f;
GLfloat yRot = 0.0f;void rendererScene(void);
void changeWindowSize(GLsizei w, GLsizei h);
void setupRC(void);
void rotateMode(int key, int x, int y);//定时刷新显示
void TimerFunc(int value)
{glutPostRedisplay();//刷新glutTimerFunc(100, TimerFunc, 1);//定时器
}int main(int argc, char* argv[])
{glutInit(&argc, argv);//设置显示模式glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);//设置窗口大小glutInitWindowSize(300, 300);//设置窗口在屏幕上的位置glutInitWindowPosition(200, 200);//创建窗口标题glutCreateWindow("三角形绘3D模型");glOrtho(-100.0f, 100.0f, -100.0f, 100.0f, -100.0f, 100.0f);//注册窗口大小改变时回调函数glutReshapeFunc(changeWindowSize);//注册点击上下左右方向按钮时回调rotateMode函数glutSpecialFunc(rotateMode);//注册显示窗口时回调渲染函数glutDisplayFunc(rendererScene);glutTimerFunc(500, TimerFunc, 1);setupRC();//消息循环(处理操作系统等的消息,例如键盘、鼠标事件等)glutMainLoop();return 0;
}/**
渲染函数
*/
void rendererScene(void)
{static float earthRotAngle = 0.0f;static float saturnRotAngle = 0.0f;static float moonRotAngle = 0.0f;BOOL bCull = TRUE;  //是否开启回溯剔除BOOL bDepth = TRUE;    //是否开启深度检测glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);if(bCull) glEnable(GL_CULL_FACE);else glDisable(GL_CULL_FACE);if(bDepth) glEnable(GL_DEPTH_TEST);else glDisable(GL_DEPTH_TEST);glMatrixMode(GL_MODELVIEW);glLoadIdentity();//glTranslatef(0.0f, 0.0f, -100.0f);//太阳glColor3ub(255, 0, 0);glutSolidSphere(10.0f, 15, 15);//火星glColor3ub(255, 200, 100);glPushMatrix();glRotatef(saturnRotAngle, 0.0f, 1.0f, 1.0f);glTranslatef(90.0f, 0.0f, 0.0f);glutSolidSphere(5.0f, 15, 15);glPopMatrix();//地球glColor3ub(0, 0, 200);glPushMatrix();glRotatef(earthRotAngle,0.0f, 1.0f, 1.0f);glTranslatef(50.0f, 0.0f, 0.0f);glutSolidSphere(7.0f, 15, 15);//月亮glColor3ub(88, 88, 88);glRotatef(moonRotAngle, 0.0f, 1.0f, 1.0f);glTranslatef(20.0f, 0.0f, 0.0f);moonRotAngle += 15.0f;if(moonRotAngle > 360.0f)moonRotAngle -= 360.0f;glutSolidSphere(2.0f, 15, 15);glPopMatrix();earthRotAngle += 10.0f;if(earthRotAngle > 360.0f)earthRotAngle -= 360.0f;saturnRotAngle += 6.0f;if(saturnRotAngle > 360.0f)saturnRotAngle -= 360.0f;glutSwapBuffers();}/**
改变窗口大小时回调函数
*/
void changeWindowSize(GLsizei w, GLsizei h)
{GLfloat length = 100.0f;if(h == 0) h = 1;glViewport(0, 0, w, h);glMatrixMode(GL_PROJECTION);glLoadIdentity();if(w <= h) glOrtho(-length, length, -length * h / w, length * h / w, -length*2.0f, length*2.0f);else glOrtho(-length * w / h, length * w / h, -length, length, -length*2.0f, length*2.0f);glMatrixMode(GL_MODELVIEW);glLoadIdentity();
}/**
设置
*/
void setupRC(void)
{//背景颜色glClearColor(0.0f, 0.0f, 0.0f, 1.0f);glShadeModel(GL_FLAT);
}/**
旋转
*/
void rotateMode(int key, int x, int y)
{if(key == GLUT_KEY_UP) xRot -= 5.0f;else if(key == GLUT_KEY_DOWN) xRot += 5.0f;else if(key == GLUT_KEY_LEFT) yRot -= 5.0f;else if(key == GLUT_KEY_RIGHT) yRot += 5.0f;if(xRot < 0) xRot = 355.0f;if(xRot > 360.0f) xRot = 0.0f;if(yRot < 0) yRot = 355.0f;if(yRot > 360.0f) yRot = 0.0f;glutPostRedisplay();}

http://chatgpt.dhexx.cn/article/0dfwO3bg.shtml

相关文章

SurfaceView、GLSurfaceView、SurfaceTexture、TextureView、SurfaceHolder、Surface

SurfaceView、GLSurfaceViewe\SurfaceTexture、TextureView、SurfaceHolder、Surface 一、简介 SurfaceTexture: SurfaceTexture是从Android3.0&#xff08;API 11&#xff09;加入的一个新类。这个类跟SurfaceView很像&#xff0c;可以从video decode里面获取图像流&#xff…

android之通过SurfaceView以及SurfaceHolder进行视频播放

使用AudioView进行视频播放的时候&#xff0c;是不是很不爽&#xff0c;千篇一律的模式&#xff0c;恶心吧。这里&#xff0c;我们可以通过一些方式对MediaPlayer进行包装。而所用到的正是SurfaceView以及SurfaceHolder。 最终效果图&#xff1a; 我们提供了四个按钮&#xff…

ANativeWindow 和 Surface

Android播放视频从解码到显示实质也是BufferQueue的生产消费的过程&#xff0c;如下图所示&#xff1a; 其中生产者是Surface&#xff0c;消费者是SurfaceFlinger。 本文主要针对Surface进行分析&#xff0c;理清ANativeWindow 和 Surface之间的关系。 ANativeWindow的定义如…

Android之Surface 与 SurfaceFlinger关系

通过前面的知识我们知道了&#xff0c;Android 系统从按下开机键到桌面&#xff0c;从桌面点击 App 图标到 Activity 显示的过程。但是 Activity 是怎么显示在屏幕上的呢&#xff1f;下面我们就来讨论下这一过程。 SurfaceFlinger 启动过程 SurfaceFlinger 进程是由 init 进程…

SurfaceView、SurfaceHolder与Surface

相关文章 SurfaceView、SurfaceHolder与SurfaceTextureView、SurfaceTexture与Surface 按照官方文档的说法&#xff0c;SurfaceView继承自View&#xff0c;并提供了一个独立的绘图层&#xff0c;你可以完全控制这个绘图层&#xff0c;比如说设定它的大小&#xff0c;所以Surf…

surfaceView、surface和sufaceHolder的关系

surfaceView、surface和sufaceHolder的关系 1.SurfaceView与Surface的联系2.SurfaceView3.Surface4.SurfaceHolder 1.SurfaceView与Surface的联系 简单来说&#xff0c;Surface是管理显示内容的数据&#xff08;implementsParcelable&#xff09;&#xff0c;包括存储于数据的…

线程天敌TerminateThread与SuspendThread http://blog.csdn.net/magictong/article/details/6304439

线程天敌TerminateThread与SuspendThread 标签&#xff1a; thread null delete dll c user 2011-04-06 13:22 10295人阅读 评论(1) 收藏 举报 分类&#xff1a; C Win32&#xff08;93&#xff09; 版权声明&#xff1a;本文为博主原创文章&#xff0c;未经博主允许不得…

511遇见易语言API模块线程挂起(SuspendThread)

线程挂起用到的API是SuspendThread 511遇见易语言模块API教程 API 暂停指定的线程 函数功能&#xff1a; 挂起线程。 参数&#xff1a; 第1个参数&#xff1a; HANDLE hThread 线程句柄。 返回值&#xff1a; 成功&#xff1a;线程的前一个挂起数。 失败&#xff1a;-1。 …

面向对象设计6大原则

概览 单一职责 1、单一职责 Single Responsibility Principle&#xff0c;简称是SRP。SRP的英文定义是&#xff1a; There should never be more than one reason for a class to change. 翻译过来的意思是&#xff1a; 应该有且仅有一个原因引起类的变更。 或许我们可以…

带你了解面向对象的设计原则

##一 摘要 今天晚上给大家介绍一下面向对象的设计原则,为什么要介绍这个呢,原因很简单,大家平时所接触的语言,无论是object-C,C,JavaScrpt,C#等都是属于面向对象语言,既然是面向对象设计语言,那么就有必要去了解一下面向对象的设计原则.那么面向对象的设计原则有哪些呢?今天来…

面向对象七大设计原则

面向对象设计的七大原则 前言 在软件开发中&#xff0c;为了提高软件系统的可维护性和可复用性&#xff0c;增加软件的可扩展性和灵活性&#xff0c;我们能要尽量根据 7 条原则来开发程序&#xff0c;从而提高软件开发效率、节约软件开发成本和维护成本。 面向对象设计原则概…

软件设计与体系——面向对象设计的原则

一&#xff1a;前言 用一道题熟悉OO设计与编程&#xff1a;给定一个4位数&#xff08;数字不完全相同&#xff09;&#xff0c;这4个数字可以得到最大4位数和最小4位数&#xff0c;算它们的差&#xff0c;不是6174则重复上述过程&#xff0c;最终会得到6174&#xff0c;验证这…

面向对象设计七大原则

面向对象设计七大原则 1) 开-闭原则 &#xff08;Open Closed Principle&#xff0c;OCP&#xff09;定义开闭原则的作用实现方法例子其它例子代码 2) 里氏代换原则&#xff08;Liskov Substitutiion Principle&#xff0c;LSP&#xff09;定义作用实现方法例子 3) 依赖倒置原则…

面向对象设计六大原则

6大原则如下&#xff1a; 1)单一职责原则,一个合理的类&#xff0c;应该仅有一个引起它变化的原因&#xff0c;即单一职责,就是设计的这个类功能应该只有一个; 优点&#xff1a;消除耦合&#xff0c;减小因需求变化引起代码僵化。 2) 开-闭原则&#xff0c;讲的是设计要对扩展…

设计模式(三)——面向对象设计原则

设计模式需要遵循基本的软件设计原则。可维护性(Maintainability)和可复用性(Reusability)是衡量软件质量的重要的两个属性: 可维护性:软件能够被理解、改正、适应及扩展的难易程度可复用性:软件能够被复用的难易程度面向对象设计的原则是支持可维护性复用,一方面需要实…

程序员必备的七大面向对象设计原则(一)

花絮 每天都在和面向对象打交道&#xff0c;但是我们在应用面向对象的时候感觉自己的面向对象技术应用的很合理&#xff1f;理解的很到位&#xff1f;应用的很到位&#xff1f;用的时候恰到好处&#xff1f;用的是否符合软件的发展趋势&#xff1f; 上面很多一连串的问题&#…

面向对象设计原则之开闭原则

开闭原则是面向对象的可复用设计的第一块基石&#xff0c;它是最重要的面向对象设计原则。开闭原则由Bertrand Meyer于1988年提出&#xff0c;其定义如下&#xff1a; 开闭原则(Open-Closed Principle, OCP)&#xff1a;一个软件实体应当对扩展开放&#xff0c;对修改关闭。即…

面向对象设计模式5大基本原则

“宇宙万物之中,没有一样东西能像思想那么顽固。” 一爱默生 首先明确模式是针对面向对象的&#xff0c;它的三大特性&#xff0c;封装、继承、多态。 面向对象设计模式有5大基本原则&#xff1a;单一职责原则、开发封闭原则、依赖倒置原则、接口隔离原则、Liskov替换原…

面向对象六大设计原则

目录 1 、单一职责&#xff08;Single Responsibility Principle&#xff09; 2 、开闭原则&#xff08;Open Close Principle&#xff09; 3、里氏替换原则&#xff08;Liskov Substitution Principle&#xff09; 4、接口隔离原则&#xff08;Interface Segregation Prin…