OpenGL (1) 入门

来源:互联网 发布:逐鹿seo军刀 编辑:程序博客网 时间:2024/04/30 09:21

         前几天跟着师兄做东西,“被迫”学习了一下OpenGl,这几天又要开始忙着作业啊,考试啊,什么的,赶紧抓个空把学到的东西记录下来,不然一个不小心就“丢”了。

         首先讨论一下开发环境配置问题,需要一些DLL、头文件、LIB文件,这些东西网上应该都有:

1)  GLU.DLL GLUT.DLLGLUT32.DLL

2)  GL.HGLAUX.HGLEXT.HGLU.HGLUT.HWGLEXT.H

3)  GLAUX.LIBGLU32.LIBGLUT32.LIBOPENGL32.LIB

 

由于OpenGL是跨平台的,因此上面的是Win平台下需要的东西。其实GLUT是一个工具包,是NateRobin开发的,他的主页上有非常详细的教程。GLUTWin的窗口、事件相关的API进行了封装,至少对于初学的我来说用起来十分舒服,而且它也被大家广泛使用的。

当然,我更喜欢原滋原味的东西,因此还是要看看Nehe的教程的。

         不说废话了,就以我目前做的一个小例子来说说OpenGL的基本框架吧,当然这里面没有用到光照、雾、纹理等高级的东西,不过有涉及到拾取。

         VS2008新建一个控制台程序,源代码如下:

#include<Windows.h>

#include<gl/glut.h>

#include<math.h>

#include<memory.h>

 

注意这里只需要有GL/GLUT.H就够了,因为其内部包含了对OPENGL的头文件的正确引用。

 

 

#define BLACK_F 0.0, 0.0, 0.0

#define RED_F 1.0, 0.0, 0.0

#define GREEN_F 0.0, 1.0, 0.0

#define  YELLOW_F 1.0, 1.0, 0.0

#define BLUE_F 0.0, 0.0, 1.0

#define WHITE_F 1.0, 1.0, 1.0

 

定义了浮点数形式的RGB数值,只是为了编程方便~  *_F”这种表示方法是模仿OpenGL的,因为OpenGL里的很多函数是根据参数的数据类型来命名最后一个的譬如glColor3f(),glColor3d(),分别表示参数为GLfloatGLdouble类型,但是其功能是一模一样的。

 

#define CUBE_NAME 1

#define PI 3.1415926

#define CUBE_SIZE 2.0

#define HEIGHT 600

#define WIDTH 800

#define SELECT_BUFF_SIZE 512

 

 

 

 

GLint wndWidth, wndHeight;

全局的变量用来记录当前的窗口宽和高

 

GLfloat angle = 0.0;                                 

//

//     Rotate Axis

//

GLfloat axis[ 3 ] = { 0.0, 0.0,0.0 };

 

//

//     Mouse location

//

GLfloat lastPos[ 3 ], currentPos[ 3 ];

 

//

// CameraProperties

//

GLfloat eye[3] = { 0.0, 0.0, 4.0};

GLfloat at[3] = { 0.0, 0.0, 0.0};            

GLfloat up[3] = { 0.0, 1.0, 0.0};           

 

 

GLfloat lastMatrix[ 16 ] =

              {1.0, 0.0, 0.0, 0.0,

              0.0, 1.0, 0.0, 0.0,

              0.0, 0.0, 1.0, 0.0,

              0.0, 0.0, 0.0, 1.0};

 

GLfloat selectCopy[ 16 ] =

              {1.0, 0.0, 0.0, 0.0,

              0.0, 1.0, 0.0, 0.0,

              0.0, 0.0, 1.0, 0.0,

              0.0, 0.0, 0.0, 1.0};

 

unsigned char btnState;

 

GLboolean isSelected;

 

 

 

 

 

 

 

//

// Projecta 2D mouse coordinates to a  3D sphere

//

int ProjectSphere( GLint x, GLint y, GLint r, GLfloat v[ 3 ])

{

       GLfloatz;

       v[0] =( GLfloat ) x  * 2.0 - ( GLfloat) r;

       v[1] =( GLfloat ) r  - ( GLfloat) y * 2.0;

       z = r * r - v[0] * v[0] - v[1] * v[1];

       if (z < 0)

       {

              return0;

       }

       v[2] =sqrt( z);

       //

       // Normalise

       //

       v[0]/= ( GLfloat ) r;

       v[1]/= ( GLfloat ) r;

       v[2]/= ( GLfloat ) r;

       return!0;

}

这个函数是将二维的鼠标坐标映射到一个单位球面上,通过它可以实现鼠标控制摄像机。

 

void Init( void )

{

       glClearColor(WHITE_F, 0.0 );

       glShadeModel(GL_FLAT );

       //glEnable(GL_DEPTH_TEST );

}

 

做一些初始化的操作glClearColor()设置了清除颜色缓冲区时所试用的颜色,glShadeModel()则指明了着色方案,你只有两种选择GL_FLAT 或者是GL_SMOOTH,默认是后者

 

void Display( void )

{

       glClear(GL_COLOR_BUFFER_BIT );

       glClear(GL_DEPTH_BUFFER_BIT );

      

         清除颜色缓冲区和深度缓冲区

 

       glColor3f(RED_F );

      

         设置了我们画东西需要用什么颜色来画

       glMatrixMode(GL_MODELVIEW );

 

         调整模型视图矩阵,这里需要特殊说明一下:

         OpenGL在完成绘制前一般有几个步骤:

1) 调整投影矩阵, 简洁来说就是把照相机放好,放到哪个位置,朝哪个方向。

2) 调整模型视图矩阵,对物体进行的一系列变换,譬如我们要让它旋转、平移等。

3) 调整视口, 摄像机看到的东西要映射到屏幕上,需要调整映射到哪里的问题。

4) 绘制

 

       glLoadIdentity();

 

       //

       //     Apply the new transform with the old one

       //

       glRotatef(angle, axis[0 ],  axis[1 ],  axis[2 ] );

       glMultMatrixf(lastMatrix );

      

    首先进行了旋转,然后乘上了之前的变换矩阵,然而由于矩阵乘法顺序的问题,实际上我们等于先做了lastMatrix的变换,然后才有旋转的效果。

 

       //

       //     Save as last Matrix

       //

       glGetFloatv(GL_MODELVIEW_MATRIX, lastMatrix);

 

       glutSolidCube(CUBE_SIZE );

    绘制函数,这里我们只是调用了一个GLUT的一个工具函数,画了一个实心的立方体,其实很多时候要绘制什么需要我们自己定义的,调用glDraw***一类的函数,然后放到glBegin()和glEnd()之间就可以了。。。

        

       glutSwapBuffers();

 

         为了实现旋转效果的平滑过渡,我们用了双缓冲,在后面的初始化里有这样的设置,当然你也可以设置成单缓冲,那么这里一定要有glFlush()

 

}

 

以上就是画好我们想要的东西,然后把这个函数设置为回调函数就可以了。那么对于用户输入的响应或者一些变换需要在下面实现:

 

void Reshape( GLsizei w, GLsizei h )

{

       glViewport(0, 0, w, h);

       glMatrixMode(GL_PROJECTION );

       glLoadIdentity();

       gluPerspective(60.0, ( GLfloat ) w/ ( GLfloat ) h,0.1, 8.0 );

       gluLookAt(eye[0], eye[1],eye[2], at[0],at[1], at[2],up[0], up[1],up[2] );

 

         对投影矩阵进行变换,之所以没有把它放到Display函数里,是因为当我们改变窗口大小的时候,会导致投影矩阵做相应的变换,同样视口也一样

        

       //

       // New windowsize

       //

       wndHeight= h;

       wndWidth= w;

}

 

GLboolean JudgeSelect( GLint x, GLint y )

{

       GLuintselectBUf[ SELECT_BUFF_SIZE];

       GLint hitNum;

       GLint viewPort[ 4 ];

       //

       // Judge ifselect the cube

       //

       glGetIntegerv(GL_VIEWPORT, viewPort);

       glSelectBuffer(SELECT_BUFF_SIZE ,selectBUf);

       glRenderMode(GL_SELECT );

 

       glInitNames();

       glPushName(0 );

 

       glMatrixMode(GL_PROJECTION );

       glPushMatrix();

 

       glLoadIdentity();

 

       //

       // Set a smallarea to be hitted

       //

       gluPickMatrix(( GLdouble ) x,( GLdouble ) ( viewPort[3 ] - y ), 5.0, 5.0, viewPort );

 

       gluPerspective(60.0, ( GLfloat ) wndWidth/ ( GLfloat ) wndHeight,0.1, 8.0 );

       gluLookAt(eye[0], eye[1],eye[2], at[0],at[1], at[2],up[0], up[1],up[2] );

      

      

       glMatrixMode(GL_MODELVIEW );

       glPushMatrix();

 

       memcpy(selectCopy, lastMatrix,16 * sizeof( lastMatrix[0 ] ) );

 

       glLoadIdentity();

 

       //

       //     Apply the new transform with the old one

       //

       glRotatef(angle, axis[0 ],  axis[1 ],  axis[2 ] );

       glMultMatrixf(lastMatrix );

 

       //

       //     Save as last Matrix

       //

       glGetFloatv(GL_MODELVIEW_MATRIX, lastMatrix);

      

       glLoadName(CUBE_NAME );

       glutSolidCube(CUBE_SIZE );

      

       glPopMatrix();

       memcpy(lastMatrix, selectCopy,16 * sizeof( lastMatrix[0 ] ) );

 

       glMatrixMode(GL_PROJECTION );

       glPopMatrix();

       glFlush();

 

       hitNum= glRenderMode( GL_RENDER);

       if( hitNum <= 0 ) {

              returnFALSE;

       } else {

              returnTRUE;

       }

 

}

这一部分是用来实现选择的,即如果用户没有选择立方体,那么拖动鼠标就不会旋转,选择的机制这里先略过,因为我还没有彻底搞懂。。。,从结果上看,就是hitNum来记录点击的次数。

 

void Mouse( GLint button, GLint state, GLint x, GLint y)

{

 

       //

       // Record mousestate

       //

       btnState= (unsigned char)  button;

       btnState<<= 4;

       btnState|= (unsigned char)state;

 

       switch(  button)

       {

       case GLUT_LEFT_BUTTON:

              //

              //Initial the lastPos

              //

              ProjectSphere(x,  y, wndHeight, lastPos );

 

              isSelected= JudgeSelect( x,y );

             

              break;

       }

}

 

这个函数也是一个回调函数,用来处理鼠标事件,当点击鼠标之后我们就要准备进行旋转了,但是也不一定,isSelected会告诉我们是否点到了立方体上,是否需要旋转。

 

void Motion( GLint x, GLint y )

{

       GLfloatd, dx, dy, dz;

 

       if( ! isSelected ) {

              return;

       }

       if ( ( ( btnState >> 4 ) & 0x0F) == GLUT_LEFT_BUTTON )

       {

              if(! ProjectSphere( x,  y,  wndHeight,  currentPos) )

              {

                     return;

              }

 

              dx= currentPos[ 0 ] - lastPos[0 ];

              dy= currentPos[ 1 ] - lastPos[1 ];

              dz= currentPos[ 2 ] - lastPos[2 ];

              //

              // Ifmouse has moved

              //

              if(dx || dy|| dz)

              {     

                     //

                     //Moving angle

                     //

                     d= sqrt( dx* dx + dy* dy + dz* dz );

                     angle=d * 180.0;

 

                     //

                     //The nomal vector of the move plane

                     //

                     axis[0]= lastPos[1] * currentPos[2]- lastPos[2] * currentPos[1];

                     axis[1]= lastPos[2] * currentPos[0]- lastPos[0] * currentPos[2];

                     axis[2]= lastPos[0] * currentPos[1]- lastPos[1] * currentPos[0];

                    

                     //

                     //Record the old coordinates

                     //

                     lastPos[0]= currentPos[0];

                     lastPos[1]= currentPos[1];

                     lastPos[2]= currentPos[2];

              }

 

              glutPostRedisplay();

       }

}

鼠标移动的回调函数,这里我们实现了旋转需要的数学运算,最关键的是记得最后要有glutPostRedisplay()这个函数,它告诉窗体要重绘。

 

GLint main( GLint argc, char** argv )

{

       glutInit(&argc, argv);

       glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB  );/* | GLUT_DEPTH);*/

       glutInitWindowSize(WIDTH, HEIGHT);

       glutInitWindowPosition(100, 100 );

       glutCreateWindow("BLIZZARD" );

         同样是一些初始化的工作,可以都一起扔到Init()函数里面。。。

       Init();

 

       glutDisplayFunc(Display );

       glutReshapeFunc(Reshape );

       glutMouseFunc(Mouse );

       glutMotionFunc(Motion );

       glutMainLoop();

         这里就是对上面所有的回调函数进行绑定。GlutMainLoop则进入消息循环,里面会有一些默认的处理机制,譬如点窗口的X,就会关掉窗口等。。。

       return 0;

}

 

         使用glut工具包,确实非常方便,简化了和系统相关的编程部分。。。另外尽量使用GL开头的数据类型,这个是跨平台的问题了。。。

         最后呢,运行的效果图像这样吧:

        

        

 

原创粉丝点击