C++博客 :: 首页 :: 联系 ::  :: 管理
  163 Posts :: 4 Stories :: 350 Comments :: 0 Trackbacks

常用链接

留言簿(48)

我参与的团队

搜索

  •  

积分与排名

  • 积分 - 393090
  • 排名 - 58

最新评论

阅读排行榜

评论排行榜

这次我将教你如何使用显示列表,显示列表将加快程序的速度,而且可以减少代码的长度。
   当你在制作游戏里的小行星场景时,每一层上至少需要两个行星,你可以用OpenGL中的多边形来构造每一个行星。聪明点的做法是做一个循环,每个循环画出行星的一个面,最终你用几十条语句画出了一个行星。每次把行星画到屏幕上都是很困难的。当你面临更复杂的物体时你就会明白了。
那么,解决的办法是什么呢?用现实列表,你只需要一次性建立物体,你可以贴图,用颜色,想怎么弄就怎么弄。给现实列表一个名字,比如给小行星的显示列表命 名为“asteroid”。现在,任何时候我想在屏幕上画出行星,我只需要调用glCallList(asteroid)。之前做好的小行星就会立刻显示 在屏幕上了。因为小行星已经在显示列表里建造好了,OpenGL不会再计算如何构造它。它已经在内存中建造好了。这将大大降低CPU的使用,让你的程序跑 的更快。
   那么,开始学习咯。我称这个DEMO为Q-Bert显示列表。最终这个DEMO将在屏幕上画出15个立方体。每个立方体都由一个盒子和一个顶构成,顶部是一个单独的显示列表,盒子没有顶。
   这一课是建立在第六课的基础上的,我将重写大部分的代码,这样容易看懂。下面的这些代码在所有的课程中差不多都用到了。

  #include <windows.h>                    // Header File For Windows
  #include <stdio.h>                     // Header File For Standard Input/Output
  #include <gl\gl.h>                     // Header File For The OpenGL32 Library
  #include <gl\glu.h>                    // Header File For The GLu32 Library
  #include <gl\glaux.h>              ?    ?// Header File For The GLaux Library

  HDC hDC=NULL;                       // Private GDI Device Context
  HGLRC hRC=NULL;                      // Permanent Rendering Context
  HWND hWnd=NULL;                      // Holds Our Window Handle
  HINSTANCE hInstance;                    // Holds The Instance Of The Application

  bool keys[256];                       // Array Used For The Keyboard Routine
  bool active=TRUE;                     // Window Active Flag Set To TRUE By Default
  bool fullscreen=TRUE;                   // Fullscreen Flag Set To Fullscreen Mode By Default

   下面设置变量。首先是存储纹理的变量,然后两个新的变量用于显示列表。这些变量是指向内存中显示列表的指针。命名为box和top。
   然后用两个变量xloop,yloop表示屏幕上立方体的位置,两个变量xrot,yrot表示立方体的旋转。

  GLuint texture[1];                     // Storage For One Texture
  GLuint box;                        // Storage For The Display List
  GLuint top;                        // Storage For The Second Display List
  GLuint xloop;                       // Loop For X Axis
  GLuint yloop;                       // Loop For Y Axis

  GLfloat xrot;                       // Rotates Cube On The X Axis
  GLfloat yrot;                       // Rotates Cube On The Y Axis

   接下来建立两个颜色数组。

  static GLfloat boxcol[5][3]=                // Array For Box Colors
  {
      // Bright: Red, Orange, Yellow, Green, Blue
      {1.0f,0.0f,0.0f},{1.0f,0.5f,0.0f},{1.0f,1.0f,0.0f},{0.0f,1.0f,0.0f},{0.0f,1.0f,1.0f}
   };

   static GLfloat topcol[5][3]=
               // Array For Top Colors
  {
      // Dark: Red, Orange, Yellow, Green, Blue
      {.5f,0.0f,0.0f},{0.5f,0.25f,0.0f},{0.5f,0.5f,0.0f},{0.0f,0.5f,0.0f},{0.0f,0.5f,0.5f}
   };

   LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
  // Declaration For WndProc

   现在正式开始建立显示列表。你可能注意到了,所有创造盒子的代码都在第一个显示列表里,所有创造顶部的代码都在另一个列表里。我会努力解释这些细节。

  GLvoid BuildLists()                    // Build Box Display List
  {

   开始的时候我们告诉OpenGL我们要建立两个显示列表。glGenLists(2)建立了两个显示列表的空间,并返回第一个显示列表的指针。“box”指向第一个显示列表,任何时候调用“box”第一个显示列表就会显示出来。

      box=glGenLists(2);                 // Building Two Lists

   现在开始构造第一个显示列表。我们已经申请了两个显示列表的空间了,并且有box指针指向第一个显示列表。所以现在我们应该告诉OpenGL要建立什么类型的显示列表。
我们用glNewList()命令来做这个事情。你一定注意到了box是第一个参数,这表示OpenGL将把列表存储到box所指向的内存空间。第二个参 数GL_COMPILE告诉OpenGL我们想预先在内存中构造这个列表,这样每次画的时候就不必重新计算怎么构造物体了。
GL_COMPILE类似于编程。在你写程序的时候,把它装载到编译器里,你每次运行程序都需要重新编译。而如果他已经编译成了.exe文件,那么每次你 只需要点击那个.exe文件就可以运行它了,不需要编译。当OpenGL编译过显示列表后,就不需要再每次显示的时候重新编译它了。这就是为什么用显示列 表可以加快速度。

      glNewList(box,GL_COMPILE);            // New Compiled box Display List

   下面这部分的代码画出一个没有顶部的盒子,它不会出现在屏幕上,只会存储在显示列表里。
   你可以在glNewList()和glEngList()中间加上任何你想加上的代码。可以设置颜色,贴图等等。唯一不能加进去的代码就是会改变显示列表的代码。显示列表一旦建立,你就不能改变它。
比如你想加上glColor3ub(rand()%255,rand()%255,rand()%255),使得每一次画物体时都会有不同的颜色。但因为 显示列表只会建立一次,所以每次画物体的时候颜色都不会改变。物体将会保持第一次建立显示列表时的颜色。 如果你想改变显示列表的颜色,你只有在调用显示列表之前改变颜色。后面将详细解释这一点。

          glBegin(GL_QUADS);            // Start Drawing Quads

               // Bottom Face

              glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f);  // Top Right Of The Texture and Quad
              glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, -1.0f, -1.0f);  // Top Left Of The Texture and Quad
              glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f);  // Bottom Left Of The Texture and Quad
              glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);  // Bottom Right Of The Texture and Quad

               // Front Face

              glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);  // Bottom Left Of The Texture and Quad
              glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f);  // Bottom Right Of The Texture and Quad
              glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f);   // Top Right Of The Texture and Quad
              glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f);   // Top Left Of The Texture and Quad

               // Back Face

              glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);  // Bottom Right Of The Texture and Quad
              glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f);  // Top Right Of The Texture and Quad
              glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f);  // Top Left Of The Texture and Quad
              glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);  // Bottom Left Of The Texture and Quad

               // Right face

              glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);  // Bottom Right Of The Texture and Quad
              glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f);  // Top Right Of The Texture and Quad
              glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f);   // Top Left Of The Texture and Quad
              glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f);  // Bottom Left Of The Texture and Quad

               // Left Face

              glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);  // Bottom Left Of The Texture and Quad
              glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);  // Bottom Right Of The Texture and Quad
              glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f);   // Top Right Of The Texture and Quad
              glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f);  // Top Left Of The Texture and Quad

          glEnd();                 // Done Drawing Quads

   用glEngList()命令,我们告诉OpenGL我们已经完成了一个显示列表。在glNewList()和glEngList()之间的任何东西就是显示列表的一部分。

      glEndList();                   // Done Building The box List

   现在我们来建立第二个显示列表。在上一个显示列表的指针上加1,就得到了第二个显示列表的指针。第二个显示列表的指针命名为“top”。

      top=box+1;                    // top List Value Is box List Value +1

   现在我们知道了第二个显示列表的指针,我们可以建立它了。

      glNewList(top,GL_COMPILE);            // New Compiled top Display List

   下面的代码画出盒子的顶部。

          glBegin(GL_QUADS);            // Start Drawing Quad

               // Top Face

              glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f);  // Top Left Of The Texture and Quad
              glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f);  // Bottom Left Of The Texture and Quad
              glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 1.0f);  // Bottom Right Of The Texture and Quad
              glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f);  // Top Right Of The Texture and Quad

          glEnd();                 // Done Drawing Quad

   然后告诉OpenGL第二个显示列表建立完毕。

      glEndList();                   // Done Building The top Display List
  }

   贴图纹理的代码和之前教程里的代码是一样的。我们需要一个可以贴在立方体上的纹理。我决定使用mipmapping处理让纹理看上去光滑,因为我讨厌看见像素点。纹理的文件名是“cube.bmp”,存放在data目录下。

  if (TextureImage[0]=LoadBMP("Data/Cube.bmp"))      // Load The Bitmap

   改变窗口大小的代码和第六课是一样的。
   初始化的代码只有一点改变,加入了一行BuildList()。请注意代码的顺序,先读入纹理,然后建立显示列表,这样当我们建立显示列表的时候就可以将纹理贴到立方体上了。

  int InitGL(GLvoid)                    // All Setup For OpenGL Goes Here
  {
       if (!LoadGLTextures())
              // Jump To Texture Loading Routine
      {
           return FALSE;
               // If Texture Didn’t Load Return FALSE
      }
       BuildLists();
                   // Jump To The Code That Creates Our Display Lists
      glEnable(GL_TEXTURE_2D);             // Enable Texture Mapping
      glShadeModel(GL_SMOOTH);             // Enable Smooth Shading
      glClearColor(0.0f, 0.0f, 0.0f, 0.5f);       // Black Background
      glClearDepth(1.0f);                // Depth Buffer Setup
      glEnable(GL_DEPTH_TEST);             // Enables Depth Testing
      glDepthFunc(GL_LEQUAL);              // The Type Of Depth Testing To Do

   接下来的三行使灯光有效。Light0一般来说是在显卡中预先定义过的,如果Light0不工作,把下面那行注释掉好了。
   最后一行的GL_COLOR_MATERIAL使我们可以用颜色来贴纹理。如果没有这行代码,纹理将始终保持原来的颜色,glColor3f(r,g,b)就没有用了。总之这行代码是很有用的。

      glEnable(GL_LIGHT0);               // Quick And Dirty Lighting (Assumes Light0 Is Set Up)
      glEnable(GL_LIGHTING);              // Enable Lighting
      glEnable(GL_COLOR_MATERIAL);           // Enable Material Coloring

   最后,设置投影校正,返回TURE。

      glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);// Nice Perspective Correction
      return TRUE;                   // Initialization Went OK

   现在来看绘画?拇搿6允掖永炊际呛芡反蟮模挥衧in,没有cos,但仍然看起来很奇怪(相信读者不会觉得头大)。首先,按惯例,清除屏幕和深度缓冲。
   然后捆绑纹理到立方体上(我知道捆绑这个词不太专业,但是……)。可以将这行放在显示列表里,但放在外边,就可以在任何时候修改它。

  int DrawGLScene(GLvoid)                  // Here’s Where We Do All The Drawing
  {
       glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  // Clear The Screen And The Depth Buffer
      glBindTexture(GL_TEXTURE_2D, texture[0]);       // Select The Texture

   现在到了真正有趣的地方了。用一个循环,循环变量用于改变Y轴位置,在Y轴上画5个立方体,所以用从1到5的循环。

      for (yloop=1;yloop<6;yloop++)           // Loop Through The Y Plane
      {

   另外用一个循环,循环变量用于改变X轴位置。每行上的立方体数目取决于行数,所以循环方式如下。

          for (xloop=0;xloop< yloop;xloop++)    // Loop Through The X Plane
          {
               glLoadIdentity();
         // Reset The View

   下边的代码是移动和旋转当前坐标系到需要画出立方体的位置。(原文有很罗嗦的一大段,相信大家的数学功底都不错,就不翻译了)

              glLoadIdentity();         // Reset The View
               // Position The Cubes On The Screen

              glTranslatef(1.4f+(float(xloop)*2.8f)-(float(yloop)*1.4f),((6.0f-float(yloop))*2.4f)-7.0f,-20.0f);

               glRotatef(45.0f-(2.0f*yloop)+xrot,1.0f,0.0f,0.0f);
  // Tilt The Cubes Up And Down
              glRotatef(45.0f+yrot,0.0f,1.0f,0.0f);  // Spin Cubes Left And Right

   然后在正式画盒子之前设置颜色。每个盒子用不同的颜色。

              glColor3fv(boxcol[yloop-1]);   // Select A Box Color

   好了,颜色设置好了。现在需要做的就是画出盒子。不用写出画多边形的代码,只需要用glCallList(box)命令调用显示列表。盒子将会用glColor3fv()所设置的颜色画出来。

              glCallList(box);         // Draw The Box

   然后用另外的颜色画顶部。搞定。

              glColor3fv(topcol[yloop-1]);   // Select The Top Color

              glCallList(top);         // Draw The Top
          }
       }
       return TRUE;
                   // Jump Back
  }

   下面的代码是键盘控制的一些东西。

              SwapBuffers(hDC);       // Swap Buffers (Double Buffering)
              if (keys[VK_LEFT])       // Left Arrow Being Pressed?
               {
  ??                 yrot-=0.2f;
      // If So Spin Cubes Left
              }
              if (keys[VK_RIGHT])      // Right Arrow Being Pressed?
              {
                   yrot+=0.2f;
      // If So Spin Cubes Right
              }
               if (keys[VK_UP])
        // Up Arrow Being Pressed?
              {
                   xrot-=0.2f;
      // If So Tilt Cubes Up
               }
              if (keys[VK_DOWN])       // Down Arrow Being Pressed?
              {
                    xrot+=0.2f;
      // If So Tilt Cubes Down
               }

   与以前的指南一样,我们要确认窗口顶部标题的正确。

              if (keys[VK_F1])       // Is F1 Being Pressed?
              {
                    keys[VK_F1]=FALSE;
   // If So Make Key FALSE
                  KillGLWindow();    // Kill Our Current Window
                  fullscreen=!fullscreen;// Toggle Fullscreen / Windowed Mode
                   // Recreate Our OpenGL Window
                  if (!CreateGLWindow("NeHe’s Display List Tutorial",
                          640,480,16,fullscreen))

                  {
                        return 0;
   // Quit If Window Was Not Created
                    }
               }
           }
       }
   }

posted on 2007-12-13 11:10 sdfasdf 阅读(547) 评论(0)  编辑 收藏 引用 所属分类: OPENGL

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理