NeHe OpenGL教程 (十二)

來源:互聯網
上載者:User

第12課
  
  顯示列表:

想知道如何加速你的OpenGL程式嗎?這一課將告訴你如何使用OpenGL的顯示列表,它通過先行編譯OpenGL命令來加速你的程式,並可以為你省去很多重複的代碼。
 
 
  
  
 這次我將教你如何使用顯示列表,顯示列表將加快程式的速度,而且可以減少代碼的長度。

當你在製作遊戲裡的小行星情境時,每一層上至少需要兩個行星,你可以用OpenGL中的多邊形來構造每一個行星。聰明點的做法是做一個迴圈,每個迴圈畫出行星的一個面,最終你用幾十條語句畫出了一個行星。每次把行星畫到螢幕上都是很困難的。當你面臨更複雜的物體時你就會明白了。

那麼,解決的辦法是什麼呢?用現實列表,你只需要一次性建立物體,你可以貼圖,用顏色,想怎麼弄就怎麼弄。給現實列表一個名字,比如給小行星的顯示列表命名為“asteroid”。現在,任何時候我想在螢幕上畫出行星,我只需要調用glCallList(asteroid)。之前做好的小行星就會立刻顯示在螢幕上了。因為小行星已經在顯示列表裡建造好了,OpenGL不會再計算如何構造它。它已經在記憶體中建造好了。這將大大降低CPU的使用,讓你的程式跑的更快。

那麼,開始學習咯。我稱這個DEMO為Q-Bert顯示列表。最終這個DEMO將在螢幕上畫出15個立方體。每個立方體都由一個盒子和一個頂部構成,頂部是一個單獨的顯示列表,盒子沒有頂。

這一課是建立在第六課的基礎上的,我將重寫大部分的代碼,這樣容易看懂。下面的這些代碼在所有的課程中差不多都用到了。

 
  
  
 下面設定變數。首先是儲存紋理的變數,然後兩個新的變數用於顯示列表。這些變數是指向記憶體中顯示列表的指標。命名為box和top。

然後用兩個變數xloop,yloop表示螢幕上立方體的位置,兩個變數xrot,yrot表示立方體的旋轉。

 
  

GLuint box;      // 儲存盒子的顯示列表
GLuint top;      // 儲存盒子頂部的顯示列表
GLuint xloop;      // X軸迴圈變數
GLuint yloop;      // Y軸迴圈變數

  
 接下來建立兩個顏色數組 
  

static GLfloat boxcol[5][3]=    // 盒子的顏色數組
{
 // 亮:紅,橙,黃,綠,藍
 {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]=    // 頂部的顏色數組
{
 // 暗:紅,橙,黃,綠,藍
 {.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}
};

  
 現在正式開始建立顯示列表。你可能注意到了,所有創造盒子的代碼都在第一個顯示列表裡,所有創造頂部的代碼都在另一個列表裡。我會努力解釋這些細節。
 
  

GLvoid BuildLists()     // 建立盒子的顯示列表
{

  
 開始的時候我們告訴OpenGL我們要建立兩個顯示列表。glGenLists(2)建立了兩個顯示列表的空間,並返回第一個顯示列表的指標。“box”指向第一個顯示列表,任何時候調用“box”第一個顯示列表就會顯示出來。
 
  

 box=glGenLists(2);    // 建立兩個顯示列表的名稱
  
 現在開始構造第一個顯示列表。我們已經申請了兩個顯示列表的空間了,並且有box指標指向第一個顯示列表。所以現在我們應該告訴OpenGL要建立什麼類型的顯示列表。

我們用glNewList()命令來做這個事情。你一定注意到了box是第一個參數,這表示OpenGL將把列表格儲存體到box所指向的記憶體空間。第二個參數GL_COMPILE告訴OpenGL我們想預先在記憶體中構造這個列表,這樣每次畫的時候就不必重新計算怎麼構造物體了。

GL_COMPILE類似於編程。在你寫程式的時候,把它裝載到編譯器裡,你每次運行程式都需要重新編譯。而如果他已經編譯成了.exe檔案,那麼每次你只需要點擊那個.exe檔案就可以運行它了,不需要編譯。當OpenGL編譯過顯示列表後,就不需要再每次顯示的時候重新編譯它了。這就是為什麼用顯示列表可以加快速度。
 
  

 glNewList(box,GL_COMPILE);   // 建立第一個顯示列表

  
 下面這部分的代碼畫出一個沒有頂部的盒子,它不會出現在螢幕上,只會儲存在顯示列表裡。

你可以在glNewList()和glEngList()中間加上任何你想加上的代碼。可以設定顏色,貼圖等等。唯一不能加進去的代碼就是會改變顯示列表的代碼。顯示列表一旦建立,你就不能改變它。

比如你想加上glColor3ub(rand()%255,rand()%255,rand()%255),使得每一次畫物體時都會有不同的顏色。但因為顯示列表只會建立一次,所以每次畫物體的時候顏色都不會改變。物體將會保持第一次建立顯示列表時的顏色。 如果你想改變顯示列表的顏色,你只有在調用顯示列表之前改變顏色。後面將詳細解釋這一點。
 
  

  glBegin(GL_QUADS);       // 開始繪製四邊形
   // 底面
   glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f); 
   glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, -1.0f, -1.0f); 
   glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f); 
   glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f); 
   // 前面
   glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f); 
   glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f); 
   glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f,  1.0f); 
   glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f,  1.0f); 
   // 後面
   glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); 
   glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f); 
   glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f); 
   glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); 
   // 右面
   glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); 
   glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f); 
   glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f,  1.0f,  1.0f); 
   glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f); 
   // 左面
   glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); 
   glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f); 
   glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f,  1.0f,  1.0f); 
   glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f); 
  glEnd();        // 四邊形繪製結束

  
 用glEngList()命令,我們告訴OpenGL我們已經完成了一個顯示列表。在glNewList()和glEngList()之間的任何東西就是顯示列表的一部分。
 
  

 glEndList();         // 第一個顯示列表結束

  
 現在我們來建立第二個顯示列表。在上一個顯示列表的指標上加1,就得到了第二個顯示列表的指標。第二個顯示列表的指標命名為“top”。
 
  

 top=box+1;         // 第二個顯示列表的名稱

  
 現在我們知道了第二個顯示列表的指標,我們可以建立它了。
 
  

 glNewList(top,GL_COMPILE);       // 盒子頂部的顯示列表

  
 下面的代碼畫出盒子的頂部。 
  

  glBegin(GL_QUADS);       // 開始繪製四邊形
   // 上面
   glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f); 
   glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f,  1.0f,  1.0f); 
   glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f,  1.0f,  1.0f); 
   glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f); 
  glEnd();        // 結束繪製四邊形

  
 然後告訴OpenGL第二個顯示列表建立完畢。 
  

 glEndList();         // 第二個顯示列表建立完畢
}

  
 貼圖紋理的代碼和之前教程裡的代碼是一樣的。我們需要一個可以貼在立方體上的紋理。我決定使用mipmapping處理讓紋理看上去光滑,因為我討厭看見像素點。紋理的檔案名稱是“cube.bmp”,存放在data目錄下。
 
  

 if (TextureImage[0]=LoadBMP("Data/Cube.bmp")) 
  
 改變視窗大小的代碼和第六課是一樣的。

初始化的代碼只有一點改變,加入了一行BuildList()。請注意代碼的順序,先讀入紋理,然後建立顯示列表,這樣當我們建立顯示列表的時候就可以將紋理貼到立方體上了。

 
  

BuildLists();      // 建立顯示列表

  
 接下來的三行使燈光有效。Light0一般來說是在顯卡中預先定義過的,如果Light0不工作,把下面那行注釋掉好了。

最後一行的GL_COLOR_MATERIAL使我們可以用顏色來貼紋理。如果沒有這行代碼,紋理將始終保持原來的顏色,glColor3f(r,g,b)就沒有用了。總之這行代碼是很有用的。
 
  

 glEnable(GL_LIGHT0);     // 使用預設的0號燈
 glEnable(GL_LIGHTING);     // 使用燈光
 glEnable(GL_COLOR_MATERIAL);    // 使用顏色材質

  
 現在到了繪製代碼的地方了,我們還是和以前一樣,以清除背景顏色為開始。

接著把紋理綁定到立方體,我可以把這些代碼加入到顯示列表中,但我還是把它留在了顯示列表外邊,這樣我可以隨便設定紋理。
 
  

int DrawGLScene(GLvoid)      // 繪製操作開始
{
 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除背景顏色

 glBindTexture(GL_TEXTURE_2D, texture[0]);  // 選擇紋理

  
 現在到了真正有趣的地方了。用一個迴圈,迴圈變數用於改變Y軸位置,在Y軸上畫5個立方體,所以用從1到5的迴圈。
 
  

 for (yloop=1;yloop<6;yloop++)    // 沿Y軸迴圈
 {

  
 另外用一個迴圈,迴圈變數用於改變X軸位置。每行上的立方體數目取決於行數,所以迴圈方式如下。
 
  

  for (xloop=0;xloop<yloop;xloop++)  // 沿X軸迴圈
  {

  
 重設模型變化矩陣 
  

   glLoadIdentity();   // 重設模型變化矩陣

  
 邊的代碼是移動和旋轉當前座標繫到需要畫出立方體的位置。(原文有很羅嗦的一大段,相信大家的數學功底都不錯,就不翻譯了)
 
  

   // 設定盒子的位置
   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); 
   glRotatef(45.0f+yrot,0.0f,1.0f,0.0f); 
  
 然後在正式畫盒子之前設定顏色。每個盒子用不同的顏色。 
  

   glColor3fv(boxcol[yloop-1]);  

  
 好了,顏色設定好了。現在需要做的就是畫出盒子。不用寫出畫多邊形的代碼,只需要用glCallList(box)命令調用顯示列表。盒子將會用glColor3fv()所設定的顏色畫出來。 
  

   glCallList(box);   // 繪製盒子

  
 然後用另外的顏色畫頂部。搞定。 
  

   glColor3fv(topcol[yloop-1]);  // 選擇頂部顏色
   glCallList(top);   // 繪製頂部
  }
 }
 return TRUE;      // 成功返回
}

  
 下面的代碼是鍵盤控制的一些東西 
  

  SwapBuffers(hDC);    // 交換緩衝
  if (keys[VK_LEFT])    // 左鍵是否按下
  {
   yrot-=0.2f;    // 如果是,向左旋轉
  }
  if (keys[VK_RIGHT])    // 右鍵是否按下
  {
   yrot+=0.2f;    // 如果是向右旋轉
  }
  if (keys[VK_UP])    // 上鍵是否按下
  {
   xrot-=0.2f;    // 如果是向上旋轉
  }
  if (keys[VK_DOWN])    // 下鍵是否按下
  {
   xrot+=0.2f;    // 如果是向下旋轉
  }

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.