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);
}
转自:http://anony3721.blog.163.com/blog/static/511974201133095555708/
相关推荐
OpenGL支持两种类型的矩阵堆栈:模型视图矩阵堆栈和投影矩阵堆栈,分别处理模型和视图空间的变换以及将模型视图空间转换到屏幕空间的过程。 2. **OpenGL中的矩阵堆栈**: - **模型视图矩阵堆栈**:用于控制对象在...
4. 在现代OpenGL中,推荐使用更灵活的矩阵管理方式,如统一着色器变量或矩阵堆栈的替代方案,因为传统的堆栈操作在某些硬件上可能效率较低。 通过理解并熟练运用`glPushMatrix()`和`glPopMatrix()`,我们可以更好地...
4. **矩阵堆栈管理**:使用glPushMatrix()和glPopMatrix()可以保存和恢复当前矩阵状态。这对于进行复杂的变换组合尤其有用,因为你可以保存当前状态,进行一些变换,然后恢复到之前的状态,而不影响其他部分。 5. *...
OpenGL是一种强大的图形编程...总结来说,这个项目展示了OpenGL矩阵堆栈的强大功能,以及如何使用它们来模拟物理现象。通过学习和分析这段代码,开发者可以深化对3D图形编程的理解,并能应用于创建更多创新的视觉效果。
* glPushMatrix, glPopMatrix:压入和弹出当前矩阵堆栈 * glPushAttrib, glPopAttrib:压入和弹出属性堆栈 * glPushClientAttrib, glPopClientAttrib:在客户属性堆栈中保存和恢复客户状态变量组 * glPushName, ...
为了有效地管理和使用这些变换矩阵,OpenGL引入了矩阵堆栈的概念。 - **定义**: 矩阵堆栈是一个用于保存和恢复矩阵状态的数据结构,特别适用于具有复杂层级结构的模型绘制,有助于提高绘图效率。 - **操作**: - `...
OpenGL使用矩阵堆栈来保存和恢复变换状态,允许复杂场景的变换操作,通过glPushMatrix()和glPopMatrix()进行管理。 四、OpenGL中的颜色、光照和材质 4.1 颜色 OpenGL中的颜色通常使用RGB或RGBA格式表示,通过...
7. **矩阵堆栈**:OpenGL提供了模型视图矩阵堆栈,方便地进行组合和撤销操作。通过`glPushMatrix()`和`glPopMatrix()`可以保存和恢复当前的模型视图矩阵,这样就可以在不干扰其他对象的情况下进行局部旋转。 8. **...
- `glMatrixMode()`:指定接下来的矩阵操作应用于哪个矩阵堆栈。 - `glLoadIdentity()`:将当前矩阵重置为单位矩阵。 - `glTranslatef()`、`glRotatef()`、`glScalef()`:执行平移、旋转和缩放的几何变换。 - `...
OpenGL提供了`glPushMatrix`和`glPopMatrix`函数来保存和恢复当前矩阵堆栈的状态。 #### 1.5 使用光照和材质 OpenGL中的光照和材质设置主要包括`glEnable`、`glDisable`、`glLight*`、`glMaterial*`等函数。 ####...
`glPushMatrix()`的作用是在OpenGL的内部堆栈上保存当前的模型视图矩阵。当你调用这个函数时,当前的矩阵会被复制并压入堆栈,这样即使后续的变换操作改变了矩阵,你还可以通过`glPopMatrix()`将矩阵恢复到之前的...
OpenGL提供了一个矩阵堆栈机制,可以用来临时存储和恢复当前的模型视图矩阵。这使得在复杂的场景中进行变换操作变得非常方便。 #### 四、OpenGL中的颜色、光照和材质 **4.1 颜色** OpenGL支持多种颜色模型,包括...
- `glPushMatrix()`和`glPopMatrix()`:保存和恢复当前矩阵堆栈,便于进行图形变换。 - `glTranslatef(0,0,-5)`:沿Z轴负方向平移5个单位。 - `glClipPlane(GL_CLIP_PLANE0,v)`:设置裁剪平面,其中`v`为裁剪平面...
最后,为了高效地使用这些变换,OpenGL使用了矩阵堆栈的概念。模型视图矩阵和投影矩阵都有自己的堆栈,允许在变换过程中保存和恢复矩阵的状态,这在需要进行复杂变换时非常有用。 示范代码中提到了绘制太阳系的示例...
变换矩阵的堆栈管理(`glPushMatrix`和`glPopMatrix`)允许你在不破坏当前视图的情况下应用多个变换。 5. **颜色变化**:颜色可以通过`glColor3f`在渲染循环中动态改变,以实现动画效果。例如,可以根据时间改变...
- **`glPushMatrix`, `glPopMatrix`**: 压入和弹出当前矩阵堆栈,用于保护和恢复矩阵的状态。 - **`glPushAttrib`, `glPopAttrib`**: 压入和弹出属性堆栈,用于保护和恢复OpenGL的状态。 - **`glPushClientAttrib`...
7. **状态管理**:OpenGL使用堆栈来管理变换、投影和视口状态,这使得可以通过`glPushMatrix`和`glPopMatrix`轻松地保存和恢复状态。 以上就是创建一个可旋转、可放大比例的OpenGL三维球体的基本过程。实践中,...
- 使用`glPushMatrix`和`glPopMatrix`来管理矩阵堆栈,避免矩阵操作之间的影响。 - 在进行复杂的图形变换时,可以利用这些函数来保存和恢复当前的状态。 3. **`glPushMatrix`和`glPopMatrix`的工作机制**: - `...