为什么调用glPushMatrix()和glPopMatrix()

本文深入解析OpenGL中glPushMatrix与glPopMatrix函数的用途与工作原理,通过示例代码演示如何在变换坐标系时正确使用这两个函数,以避免坐标位置错误,并介绍如何利用它们进行光源位置的灵活调整。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近在研究有关opengl的相关内容,一直不明白glPushMatrix()glPopMatrix() 这两个函数为什么要配对使用,在网上查了一下,已经有很多网友有这方面的解答,转载一篇文章给大家分享。

首先, 在opengl中,函数glTranslatef的作用就是移动坐标原点。对应的3个参数对应着3个坐标轴。就是使绘图坐标系相对世界坐标系沿x,y,z轴移动x,y,z个单位。

如果你调用一次glTranslatef(1.0f,0.0f,0.0f)然后画一个小球,接着再调用次glTranslatef(0.0f,1.0f,0.0f)再画一个小球。
此时,两个小球中,一个在另外一个正右方。
所以,如果要使两个小球分别处于x、y轴,则需要在第二次画之前调用glLoadIdentity()函数,使坐标原点归位。
另外,此处的坐标系为右手坐标系。
切记切记。
glLoadIdentity()将当前的矩阵清零,不可再恢复;就是令当前绘图坐标系从新回到世界坐标系的位置,另他们重合。
  
glPushMatrix()将当前矩阵压入栈中,当前矩阵不清零,其下的操作及显示在当前的矩阵下继续进行,在使用glPopMatrix()后,当前的矩阵恢复到调用glPushMatrix()之前,在两者之间的各种变换不在起作用。  


glPushMatrix、glPopMatrix操作其实就相当于栈里的入栈和出栈。

例如你当前的坐标系原点在你电脑屏幕的左上方。现在你调用glPushMatrix,然后再调用一堆平移、旋转代码等等,然后再画图。那些平移和旋转都是基于坐上角为原点进行变化的。而且都会改变坐标的位置,经过了这些变化后,你的坐标肯定不再左上角了。那如果想恢复怎么办呢?这时就调用glPopMatrix从栈里取出一个“状态”了,这个状态就是你调用glPushMatrix之前的那个状态。就如很多opengl的书上所讲:调用glPushMatrix其实就是把当前状态做一个副本放入堆栈之中。

  
常规说来,顺序的调用多个glRotatef函数,这些旋转效果就会是叠加的不是独立的了。glRotatef(angle,x,y,z):和glTranslatef()属于一类函数,glTranslatef()是平移,glRotatef是旋转,就是使当前绘图坐标系绕世界坐标系的x,y,z旋转angle个角度,x,y,z的值非0既1,比如glRotatef(30,1.0f,0.0f,0.0f)就是绕x轴旋转30度,glRotatef(30,1.0f,1.0f,0.0f)就是绕x,y的夹角线旋转30度。


接下来的内容来自:http://www.cppblog.com/doing5552/archive/2009/01/08/71531.html


今天忽然感悟到为什么在进行变换之前要用glPushMatrix();这个函数,而在变换完毕后有用glPopMatrix()这两个函数了,赶紧记下来:

    我们在变换坐标的时候,使用的是glTranslatef(),glRotaef()等函数来操作,操作的是什么呢?操作的是当前矩阵,我们也知道,这些坐标变换(翻转,旋转也好)都是通过操作矩阵来实现的,而矩阵相乘是会叠加的,当你用完一个变换函数后,当前操作的矩阵就被改变了,当你还停留在变换以前的思维,我在这个地方绘制恰好是我想要的时候,你会发现再绘制出来的不是在你想要的位置,因为你在操作变换的时候,当前矩阵被改变了。

   比如你在默认情况下在原点画了一个球,然后又进行了一个变换,比如用glTranslatef( 0.0, 0.0, 1.0 );沿z轴移动一定距离又画了一个球,然后你想再在原点画一个大一点的球覆盖原来的那个,当你绘制的时候就会发现,你现在绘制的球已不在你想像的地方了。

我们来做个实验:

代码如下:

void display()
{
glClear( GL_COLOR_BUFFER_BIT );
glShadeModel( GL_SMOOTH );

//现在原点绘制一个红色正方形
glColor3f( 1.0, 0.0, 0.0 );
glRectf( -0.05, -0.05, 0.05, 0.05 );

//glPushMatrix();

//变换--沿x轴移动
    glTranslatef( 0.2, 0.0, 0.0 );

    //glPopMatrix();

//再绘制一个正方形
glColor3f( 0.0, 1.0, 0.0 );
glRectf( -0.05, -0.05, 0.05, 0.05 );//这时,当我们还想在同样位置绘制时,却发现已经偏移

glFlush();
}

 

    当我们把glPushMatrxi()和glPopMatrix()注释掉以后我们发现,当我们再想在同样的位置绘制一个正方形的时候,就会发现已经按我们的glTransfef()所指定的沿x轴偏移了0.2个单位。

而当我们不把两句函数调用注释掉时,运行发现,绿色的正方形覆盖了原来的红色的正方形。

 

所以,这两个函数的压栈弹栈是有用地~~~~~~~~~~

这两个函数的具体的执行方式就不扯了,网上n多。

知之为知之,不知百度之

~~~~~~~~~~~~吼吼~~~~~~~~~~

续文:

顿悟这点以后,晚上又突然想明白了另一个大问题:移动光源的位置。

在顿悟以前,总觉得光源该怎么移动呢?那不是十分十分麻烦么,而且不知道怎么办,现在明白了这个道理以后,光照的移动就简单了。

移动方式:

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

测试代码:

#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 };

glPushMatrix();
glTranslatef( 0.0, 0.0, -5.0 );

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();

glutSolidSphere( 0.5, 40, 40 );//被光照的物体
glPopMatrix();
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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值