http://anony3721.blog.163.com/blog/static/511974201133095555708/

题外话:学习OpenGL有两个礼拜了,总结下才发现仅仅明白了其基本概念。光是GDI绘图中的窗口、视口以及设备环境的概念,就足足困扰我了一个礼拜,其实明白了只后发现一句话就描述清楚了,根本不像MSDN和google上讲的难的很。高手们可能是不屑于基础概念的讲解或者根本不愿意讲解,所以我在google上看了半天都没有明白为什么要使用这两个函数。这给和我一样的初学者势必造成巨大的入门障碍,使得高手是同样的高,菜鸟各有各的菜!为了报答国土恩,让我国学习OpengL的人能尽快变成高手,提高我国的科技水平。本人在理解了困扰我的概念后,将它们免费的且详尽的记录在我的博客中,希望给初学者指条明路,对大家有帮助。

1.原理讲解

终于明白为什么使用glPushMatrix()和glPopMatrix()的原因了。将本次需要执行的缩放、平移等操作放在glPushMatrix和glPopMatrix之间。glPushMatrix()和glPopMatrix()的配对使用可以消除上一次的变换对本次变换的影响。使本次变换是以世界坐标系的原点为参考点进行。下面对上述结论做进一步的解释:

1)OpenGL中的modelview矩阵变换是一个马尔科夫过程:上一次的变换结果对本次变换有影响,上次modelview变换后物体在世界坐标系下的位置是本次modelview变换的起点。默认时本次变换和上次变换不独立。

2)OpenGL物体建模实际上是分两步走的。第一步,在世界坐标系的原点位置绘制出该物体;第二步,通过modelview变换矩阵对世界坐标系原点处的物体进行仿射变换,将该物体移动到世界坐标系的目标位置处。

3)将modelview变换放在glPushMatrix和glPopMatrix之间可以使本次变换和上次变换独立。

4)凡是使用glPushMatrix()和glPopMatrix()的程序一般可以判定是采用世界坐标系建模。既世界坐标系固定,modelview矩阵移动物体。

一般说来,矩阵堆栈常用于构造具有继承性的模型,即由一些简单目标构成的复杂模型。例如,一辆自行车就是由两个轮子、一个三角架及其它一些零部件构成的。它的继承性表现在当自行车往前走时,首先是前轮旋转,然后整个车身向前平移,接着是后轮旋转,然后整个车身向前平移,如此进行下去,这样自行车就往前走了。将上述模型的构造过程放在glPushMatrix和glPopMatrix之间,则本次汽车在世界坐标系中的位置不是基于上一次汽车的位置而给出的(以前一次的位置为参考),而是直接给出的以世界下的坐标(以世界坐标系的原点为参考)。整个过程是符合人的思维过程的,由于每次建模都是以单位阵为变换起点,故便于采用统一的实现方式进行处理。

矩阵堆栈对复杂模型运动过程中的多个变换操作之间的联系与独立十分有利。因为所有矩阵操作函数如glLoadMatrix()、glMultMatrix()、glLoadIdentity()等只处理当前矩阵或堆栈顶部矩阵,这样堆栈中下面的其它矩阵就不受影响。堆栈操作函数有以下两个:
          void glPushMatrix(void);

void glPopMatrix(void);

第一个函数表示将所有矩阵依次压入堆栈中,顶部矩阵是第二个矩阵的备份;压入的矩阵数不能太多,否则出错。第二个函数表示弹出堆栈顶部的矩阵,令原第二个矩阵成为顶部矩阵,接受当前操作,故原顶部矩阵被破坏;当堆栈中仅存一个矩阵时,不能进行弹出操作,否则出错。由此看出,矩阵堆栈操作与压入矩阵的顺序刚好相反,编程时要特别注意矩阵操作的顺序。为了更好地理解这两个函数,我们可以形象地认为glPushMatrix()就是“记住自己在哪”,glPopMatrix()就是“返回自己原来所在地”。

2. 举例

例1. OpenGL光源位置的移动

移动方式: 先pushMatrix()一下,然后在进行移动操作,然后旋转操作,然后指定光源的位置,然后PopMatrix()一下,就完成了。

#include "windows.h"
#include <gl/glut.h>

static int spin = 0;

void init()
{
glShadeModel( GL_SMOOTH );
glEnable( GL_LIGHTING );
glEnable( GL_LIGHT0 );
glEnable( GL_DEPTH_TEST );

}

void display()
{
 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
 
 GLfloat position[] = { 0.0, 0.0, 1.5, 1.0 }; 
 //第一点也是最重要的一点:OpenGL中的模型视图变换矩阵全是右乘当前变换矩阵
 glPushMatrix();  //将当前变换矩阵(单位阵)压入堆栈
 glTranslatef( 0.0, 0.0, -5.0 );     // transformation 1
 glPushMatrix();  //将平移变换后的的矩阵作为当前变换矩阵压入堆栈,
 glRotated( (GLdouble)spin, 1.0, 0.0, 0.0 );
 glLightfv( GL_LIGHT0, GL_POSITION, position );
 glTranslated( 0.0, 0.0, 1.5 );
 glDisable( GL_LIGHTING );
 glColor3f( 0.0, 1.0, 0.0 );
 glutWireCube( 0.1 );//绿色的下框,代表光源位置
 glEnable( GL_LIGHTING );
 glPopMatrix();  //消除绘制绿色WireCube时对当前变换矩阵的影响

glutSolidSphere( 0.5, 40, 40 );//以被光照的物体
 glPopMatrix(); // Pop the old matrix without the transformations.   //返回到单位矩阵
 glFlush();
}

void reshape( int w, int h )
{
 glViewport( 0, 0, (GLsizei)w, (GLsizei)h );
 glMatrixMode( GL_PROJECTION );
 glLoadIdentity();
 gluPerspective( 40.0, (GLfloat)w/(GLfloat)h, 1.0, 20.0 );
 glMatrixMode( GL_MODELVIEW );
 glLoadIdentity();
}

void mouse( int button, int state, int x, int y )
{
 switch ( button )
 {
 case GLUT_LEFT_BUTTON:
  if ( state == GLUT_DOWN )
  {
   spin = ( spin + 30 ) % 360;
   glutPostRedisplay();
  }
  break;
 default:
  break;
 }
}

int main( int argc, char ** argv )
{
 glutInit( &argc, argv );
 glutInitDisplayMode( GLUT_RGB | GLUT_SINGLE | GLUT_DEPTH );
 glutInitWindowPosition( 100, 100 );
 glutInitWindowSize( 500, 500 );
 glutCreateWindow( argv[0] );
 init();
 glutDisplayFunc( display );
 glutReshapeFunc( reshape );
 glutMouseFunc( mouse );
 glutMainLoop();
 return 0;
}

例2  机械手臂的旋转

下面例子中的机械手臂是由两个简单的长方体依据一定的继承关系构成的。glPushMatrix和glPopMatrix之间的变换相对前一次是独立的

#include "windows.h"
#include <GL/gl.h>

#include <GL/glu.h>

#include <GL/glaux.h>

void myinit(void);

void drawPlane(void);

void CALLBACK elbowAdd (void);

void CALLBACK elbowSubtract (void);

void CALLBACK shoulderAdd (void);

void CALLBACK shoulderSubtract (void);

void CALLBACK display(void);

void CALLBACK myReshape(GLsizei w, GLsizei h);

static int shoulder = 0, elbow = 0;

void CALLBACK elbowAdd (void)

{
   elbow = (elbow + 5) % 360;

}

void CALLBACK elbowSubtract (void)

{
   elbow = (elbow - 5) % 360;

}

void CALLBACK shoulderAdd (void)

{
   shoulder = (shoulder + 5) % 360;

}

void CALLBACK shoulderSubtract (void)

{
   shoulder = (shoulder - 5) % 360;

}

void CALLBACK display(void)

{
   glClear(GL_COLOR_BUFFER_BIT);
   glColor3f(0.0, 1.0, 1.0);

glPushMatrix(); // 将此句注释掉后可以发现上一次的变换结果对当前变换有影响,加上了glPushMatrix的目的是让各次变换相互独立。

glTranslatef (-0.5, 0.0, 0.0); // 将旋转后的Wirebox向左移动0.5个单位
   glRotatef ((GLfloat) shoulder, 0.0, 0.0, 1.0);   //看到shoulder是相对于0的绝对角度,不是基于上一次位置的相对角度。
   glTranslatef (1.0, 0.0, 0.0); //Step 1将WireBox向右移动一个单位
   // void auxWireBox(GLdouble width,GLdouble height,GLdouble depth)

auxWireBox(2.0, 0.2, 0.5);  //这个WireBox以x=1为中心,width=2从该中心开始向两边各延伸1个单位。

glTranslatef (1.0, 0.0, 0.0);
   glRotatef ((GLfloat) elbow, 0.0, 0.0, 1.0);
   glTranslatef (0.8, 0.0, 0.0);
   auxWireBox(1.6, 0.2, 0.5);

glPopMatrix(); // 可以看出glPushMatrix和glPopMatrix之间的变换效果被消除。清除上一次对modelview矩阵的修改。
   glFlush();

}

void myinit (void)

{
   glShadeModel (GL_FLAT);

}

void CALLBACK myReshape(GLsizei w, GLsizei h)

{
   glViewport(0, 0, w, h);
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   gluPerspective(65.0, (GLfloat) w/(GLfloat) h, 1.0, 20.0);
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
   glTranslatef (0.0, 0.0, -5.0);  /* 认为是viewing transform 不好理解,因此时是物体不动,世界坐标系向z轴正方向移动5个单位,眼睛位于世界坐标系的原点; 此处理解为对模型的变换更加直观既将物体向负z轴移动5个单位。*/
 }

void main(void)

{
   auxInitDisplayMode (AUX_SINGLE | AUX_RGBA);
   auxInitPosition (0, 0, 400, 400);
   auxInitWindow ("Composite Modeling Transformations");
   myinit ();
   auxKeyFunc (AUX_LEFT, shoulderSubtract);
   auxKeyFunc (AUX_RIGHT, shoulderAdd);
   auxKeyFunc (AUX_UP, elbowAdd);
   auxKeyFunc (AUX_DOWN, elbowSubtract);
   auxReshapeFunc (myReshape);
   auxMainLoop(display);
 
}

OpenGL使用矩阵堆栈glpushMatrix的原因相关推荐

  1. 《高效学习OpenGL》之 操作矩阵堆栈 glPushMatrix(),glPopMatrix()

    glPushMatrix(); //把当前堆栈中的所有矩阵都下压一级.当前矩阵堆栈是由glMatrixMode()指定的. glPopMatrix(); //把堆栈顶部的那个矩阵弹出堆栈,销毁被弹出的 ...

  2. OpenGL之矩阵堆栈绘制立体图元

    绘制流程 一.main函数 初始化双缓冲窗口:glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL); 设置w ...

  3. OpenGL入门 矩阵堆栈实现简单行星系统

    本文介绍如何使用矩阵堆栈原理实现简单的行星运行系统. 原理 有时我们需要在一个场景中绘制不同的模型,如果这些模型彼此间没有联系,即各模型的位置不会相互影响,那我们只需要单独为每个模型创建合适的变换矩阵 ...

  4. OpenGL学习笔记:模型变换、视图变换、投影变换 、视口变换、操作矩阵堆栈

    1. 模型变换和视图变换  从"相对移动"的观点来看,改变观察点的位置与方向和改变物体本身的位置与方向具有等效性.在OpenGL中,实现这两种功能甚至使用的是同样的函数. 由于模型 ...

  5. OpenGL之矩阵相乘

    单位矩阵 主对角线上数据都是1,其余元素都是0,即为单元矩阵. 向量 X 单元矩阵 = 向量 X 1,不会发生任何变化. 向量与单元矩阵相乘的前提是:向量的列数 == 单元矩阵的行数 矩阵分类 行优先 ...

  6. OpenGL纹理矩阵,alpha混合和丢弃

    OpenGL纹理矩阵,alpha混合和丢弃 先上图,再解答. 完整主要的源代码 源代码剖析 先上图,再解答. 完整主要的源代码 #include <stdio.h> #include &q ...

  7. C++ opengl 对OpenGL中矩阵设置的初步认识

    对OpenGL中矩阵设置的初步认识: 在入门级的代码里面,通常在初始化OpenGL中可以看到这样的代码: void init(){glMatrixMode(GL_PROJECTION);gluPers ...

  8. 导致堆栈溢出的原因什么?

    导致堆栈溢出的原因: 1. 函数调用层次太深.函数递归调用时,系统要在栈中不断保存函数调用时的现场和产生的变量,如果递归调用太深,就会造成栈溢出,这时递归无法返回.再有,当函数调用层次过深时也可能导致 ...

  9. OpenGL投影矩阵与相机模型(Set Projection Matrix for Pinhole Camera Model)

    出处:http://oliver.zheng.blog.163.com/blog/static/1424115952013349234838/ (转载自"结冰的雪"百度空间) 目的 ...

最新文章

  1. (POJ-3279)Fliptile (dfs经典---也可以枚举)
  2. bzoj 1864: [Zjoi2006]三色二叉树
  3. Nginx —— nginx的命令行控制(nginx的启动与停止、重载配置文件、回滚日志文件、平滑升级等操作)
  4. iOS开发缓存机制之—内存缓存机制
  5. ubuntu文本模式获得权限修改profile
  6. 【CF1344D】Résumé Review【数学】【二分】
  7. 人才梯队的搭建:55页集团人才梯队建设方案,梯队人员的管理
  8. 继电保护原理7-母线保护
  9. JDK1.8下载、安装和环境配置教程
  10. Android-Binder机制
  11. 中国黑客传说:游走在黑暗中的精灵
  12. thymeleaf引用图片_thymeleaf中img标签图片src路径问题
  13. angularJs 页面筛选标签小功能
  14. 常用IP相关命令查询
  15. html自动适应屏幕分辨率,关于HTML的屏幕分辨率适配的方法,求主流HTML自适应方法。...
  16. LC91 Decode Ways
  17. 微积分入门书籍(二)
  18. 武磊离顶级前锋到底有多远?
  19. Vue3封装Video.js组件(基于video.js)
  20. Openpose Usage 笔记

热门文章

  1. Springboot实现Web聊天室
  2. 上位机开发 常用的平台及语言
  3. MySQL 排序规则简介
  4. 【ConfluxNews】2023.3.13 浙江卫视联合有哇宇宙发行基于树图Conflux区块链的《我们的客栈》数字文创
  5. Statement 和 PrepareStatement执行SQL
  6. 谁还写周报啊?Python小工具发个周报邮件给老板就行
  7. 【题解】洛谷P2114 [NOI2014]起床困难综合症
  8. 什么是接口?什么是接口测试?什么是接口自动化测试?
  9. 2021南京icpc J
  10. 21,spark sql 测试 : 1.4G 文件实战,测试耗时多少,先分区,再在分区内计算,用列内容分区( 这是一个很魔幻的问题 ),自定义分区