語法
void WINAPI glPushMatrix(void);
將當前矩陣保存入堆疊頂(保存當前矩陣)。
用法/舉例
glPushMatrix 函式將當前矩陣堆疊推送,通過一個,複製當前矩陣。 這就是後 glPushMatrix 的調用堆疊的頂部矩陣是它下面的相同的。
原理講解
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()就是“返回自己原來所在地”。
舉例
1.舉例
例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);
}