opengl pushmatrix(gltranslatef函数)

http://anony3721.blog.163.com/blog/static/511974201133095555708/题外话:学习OpenGL有两个礼拜了,总结下才发现仅仅明白了其基本概念。光是GDI绘图中的窗口、视口以及设备环境的概念,就足足困扰我了一个礼拜,其实明白了只后发现一句话就描述清楚了,根本不像MSDN和google上讲的难的很。高手们可能是不屑于基础概念的讲解或者根本不

大家好,又见面了,我是你们的朋友全栈君。

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

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/126542.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • JAVA基于dom4j实现对XML操作「建议收藏」

    JAVA基于dom4j实现对XML操作「建议收藏」该篇博客写java基于dom4j来操作xml的一些基本实现,需要用到以下jar包该篇博客目录1、XML特点、语法规则2、XML与HTML区别3、基于dom4j下java实现对XML基本操作一、XML特点、语法规则1、XML特点XML是一种标记语言,很类似HTMLXML的设计宗旨是传输数据,而非显示数据(HTML)XML标签没…

  • phpstrom2021激活码 3月最新注册码

    phpstrom2021激活码 3月最新注册码,https://javaforall.cn/100143.html。详细ieda激活码不妨到全栈程序员必看教程网一起来了解一下吧!

  • 秒懂mysql中的group by用法

    秒懂mysql中的group by用法文章转载自:https://blog.csdn.net/u014717572/article/details/80687042先来看下表1,表名为test:执行如下SQL语句:SELECTnameFROMtestGROUPBYname你应该很容易知道运行的结果,没错,就是下表2:可是为了能够更好的理解“groupby”多个列“和”聚合函数“的应用,我建议在思考的过程中…

  • scp命令传文件

    scp命令传文件SCP进行文件传输1、基本语法:2、具体用法1、从本地将文件传输到服务器2、从本地将文件夹传输到服务器3、将服务器上的文件传输到本地4、将服务器上的文件夹传输到本地1、基本语法:Linuxscp命令用于Linux之间复制文件和目录。scp是securecopy的缩写,scp是linux系统下基于ssh登陆进行安全的远程文件拷贝命令。scp是加密的,rcp是不加密的,scp是rcp的加强版。scp【本地或远程文件的路径】【服务器用户名】@【服务器地址】:【远程或

  • 递归算法–斐波那契数列「建议收藏」

    递归算法–斐波那契数列「建议收藏」大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。n&amp;lt;=39很容易我们想到使用递归求解:publicclassSolution{publicintFibonacci(intn){if(n==0)return0;if(n==1)…

  • origin多组柱状图_柱状图上下两组数据

    origin多组柱状图_柱状图上下两组数据今日份知识你摄入了么?在我的数据分析生涯中,我几乎完全是在SQL中锻炼我的技能。虽然SQL可以做一些非常酷的事情,但它有它的局限性-这些限制在很大程度上让我决定了要不要去感受数据科学别的巨大魅力。在我之前的数据岗位中,我需要分析来自外部源的数据文件和工具,但这些工具限制了它可以处理的数据量或花费过多的时间,使任务变得几乎不可能彻底完成。在我所有职位中有一个数据pipeline有点像这样:…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号