電腦遊戲製作開發設計論壇 首頁 電腦遊戲製作開發設計論壇
任何可以在PC上跑的遊戲都可以討論,主要以遊戲之製作開發為主軸,希望讓台灣的遊戲人有個討論、交流、教學、經驗傳承的園地
 
 常見問題常見問題   搜尋搜尋   會員列表會員列表   會員群組會員群組   會員註冊會員註冊 
 個人資料個人資料   登入檢查您的私人訊息登入檢查您的私人訊息   登入登入 

Google
OpenGL入門教學(08)

 
發表新主題   回覆主題    電腦遊戲製作開發設計論壇 首頁 -> 遊戲程式高級班:DirectX、OpenGL及各種圖型函式庫
上一篇主題 :: 下一篇主題  
發表人 內容
還是零分
散播福音的祭司


註冊時間: 2007-09-19
文章: 164

653.83 果凍幣

發表發表於: 2008-7-10, PM 4:42 星期四    文章主題: OpenGL入門教學(08) 引言回覆

接著上一篇的內容
要繼續講材質貼圖的多解析度材質部分

同樣一張圖
在畫面上有時放大,有時縮小
對於這種縮放時的處理在上一篇的內容中祭出了"GL_NEAREST"和"GL_LINEAR"來處理

在放大的時候用"GL_LINEAR"做線性內插其實效果已經很讓人滿意了
但若是縮小的情況下無論是用"GL_NEAREST"或是"GL_LINEAR"都不大對勁
圖縮的越小越怪,而且有閃爍感


解決的方法是對同樣一張圖製作不同解析度的版本
例如原圖是128×128的話,那就再提供同一張圖的64×64、32×32、16×16、一直到1×1的版本
自己動手改良的縮小圖會比較像樣
電腦縮小圖片到一個地步就會換較小解析度版本的圖片
玩遊戲的時候應該注意過這種現象才對
使用Google_Earth這個Google的軟體時更明顯


那麼...不能再只使用一張圖片了
可能需要載入N張圖片,你可能會想用這種方式存:
unsigned char 圖檔1 = LoadBitmapFile( 圖片01.bmp );
unsigned char 圖檔2 = LoadBitmapFile( 圖片02.bmp );
unsigned char 圖檔3 = LoadBitmapFile( 圖片03.bmp );
unsigned char 圖檔4 = LoadBitmapFile( 圖片04.bmp );
unsigned char 圖檔5 = LoadBitmapFile( 圖片05.bmp );
unsigned char 圖檔6 = LoadBitmapFile( 圖片06.bmp );

要使用時才用glTexImage2D()載入材質

這種存法等於是把圖存放到主記憶體中
要用時才讀進顯示卡的記憶體內

不斷讀取材質會拖慢速度
不如把該用的材質一口氣放進顯示卡的記憶體內

為此OpenGL提供材質物件來存放和管理圖檔,把圖全交到顯示卡手裡

使用材質物件的做法(不是真的程式碼,只是方便說明)
代碼:

unsigned int 材質物件[張數];        //宣告一個陣列來取得材質物件們的編號
glGenTextures( 張數 , 材質物件 );   //建立物件了,編號也到手了

//0是隨便打的,舉個例而已,接在glBindTexture()之後的材質資訊會
//被存進"材質物件[0]"所代表的材質物件中
glBindTexture( GL_TEXTURE_2D , 材質物件[0] );
glTexParameteri( ~ );                        //材質控制
glTexImage2D( ~ );                           //這裡用的圖會放進材質物件

上面的東西寫成副程式會比較好




回到多解析度材質這件事上
在上上篇中寫到:
//------glTexImage2D( GL_TEXTURE_2D , level , component , 圖片寬度 , 圖片高度 , border , GL_RGB , GL_UNSIGNED_BYTE , image );
//
//level:目前設為零,下下篇使用到多解析度材質時會用到,level是其他解析度材質的編號



就是這個了!
放置多解析度材質要這樣放:
glTexImage2D( GL_TEXTURE_2D , 0 ,,,,,,, 圖檔128×128版);
glTexImage2D( GL_TEXTURE_2D , 1 ,,,,,,, 圖檔64×64版 );
glTexImage2D( GL_TEXTURE_2D , 2 ,,,,,,, 圖檔32×32版 );
glTexImage2D( GL_TEXTURE_2D , 3 ,,,,,,, 圖檔16×16版 );


glTexImage2D( GL_TEXTURE_2D , 7 ,,,,,,, 圖檔1×1版 );

知道為什麼稱第二個參數叫level了吧

如果不想自己準備各種解析度圖片的話
可以用gluBuild2DMipmaps()這個函式代勞,我偏向使用這個函式,自己準備多解析度材質很累


上上篇還有講到:
//------glTexParameteri(GL_TEXTURE_2D, pname , param );
//
//第二個參數如果是用"GL_TEXTURE_MIN_FILTER"的話,則是表示要決定圖片縮小時的處理
//處理方法用第三個參數決定,一樣有"GL_NEAREST","GL_LINEAR"兩個可選
//除了那兩個之外,縮小時的處理還有其他方法,不過那是多解析度材質的選項了,下下篇再講


除了"GL_NEAREST"和"GL_LINEAR"的其他方法指的就是:
GL_NEAREST_MIPMAP_NEAREST
GL_NEAREST_MIPMAP_LINEAR
GL_LINEAR_MIPMAP_NEAREST
GL_LINEAR_MIPMAP_LINEAR

寫在MIPMAP之前的NEAREST和LINEAR是對材質做最接近處理和線性內插
跟原本的"GL_NEAREST"和"GL_LINEAR"意思相同

寫在MIPMAP之後的NEAREST和LINEAR則是對不同解析度材質間的處理
看是要選擇使用解析度最靠近的材質影像
還是將兩張解析度材質做線性內插

用說的也說不清楚,還是自己動手試試看最好



如果要放入顯示卡的材質物件太多時,會被移出顯示卡
不常用的材質物件被移出就算了
要是常用的材質物件進進出出的就會影響效能
有必要分清楚哪張材質物件比較重要

所以OpenGL提供這個函式

//------glPrioritizeTextures( 張數 , 材質物件 , priorities);
//
//前兩個參數和glGenTextures( 張數 , 材質物件 )一模一樣,放入的值也一樣
//第三個參數是個陣列的指標
//陣列的元素和張數一樣多,值介於1到0之間
//被設為1的材質物件最重要
//被設為0的材質物件最不重要,顯示卡擠不下了第一個就踢它



代碼:

//-----------------------------------------------------------------------------
//                                                              2008/7/5
//                             Texture Object
//                                                              by還是零分
//-----------------------------------------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <wingdi.h>
#include <math.h>
#include <GL\glut.h>
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")//讓console視窗消失,反正開全螢幕也看不到了(Visual限定)

int WinNumber=NULL;                              //用來放置視窗代碼
int old_rot_x = 0,   old_rot_y = 0;                  //剛按下滑鼠時的視窗座標
int rot_x = 0,      rot_y = 0;                     //拖曳後的相對座標,用這決定要旋轉幾度
int record_x = 0,   record_y = 0;                  //紀錄上一次旋轉的角度

float distance = 0;                              //在平移矩陣中使用
float light_position[] = { -20, 20, 0};               //光源的位置

unsigned int textures[6];                        //用來儲存6張材質物件的編號
float priorities[6]={1.0,0.8,0.6,0.4,0.2,0.0};         //刪除材質物件的順序

void WindowSize(int , int );                     //負責視窗及繪圖內容的比例
void Keyboard(unsigned char ,int ,int );            //獲取鍵盤輸入
void Mouse(int ,int ,int ,int );                  //獲取滑鼠按下和放開時的訊息
void MotionMouse(int ,int );                     //獲取滑鼠按下期間的訊息
void Display(void);                              //描繪

void SetLightSource(void);                        //設定光源屬性
void SetMaterial(void);                           //設定材質屬性
void Texture(void);                              //處理材質貼圖的相關指令
unsigned char *LoadBitmapFile(char *, BITMAPINFO *);   //用來將BMP圖檔讀入並改為RGB格式
void SetTexObj(char *,int );                     //將圖片放入材質物件中,第二個參數能指定材質物件

int main()
{
   glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB );
   glutInitWindowSize( 600, 600);                  //視窗長寬
   glutInitWindowPosition( 400, 100);               //視窗左上角的位置
   WinNumber = glutCreateWindow( "這裡是視窗標題" );   //建立視窗
   glutFullScreen();                           //全螢幕模式

   Texture();

   //下面五個是用來指定Callback函數
   glutReshapeFunc ( WindowSize );
   glutKeyboardFunc( Keyboard );
   glutMouseFunc   ( Mouse );
   glutMotionFunc  ( MotionMouse );
   glutDisplayFunc ( Display );

   SetLightSource();
   SetMaterial();

   glutMainLoop();

   return 0;
}

void Display(void)
{
   glClearColor(0.0, 0.0, 0.0, 1.0);                     //用黑色塗背景
   glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
   glPolygonMode (GL_BACK, GL_LINE);                     //設定面的背面用線條顯示
   
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();

   gluLookAt( 0, 0, 30.0, 0, 0, 0, 0, 1, 0);               //視線的座標及方向
   glTranslatef( 0, 0, distance);                        //沿著z軸平移
   glRotatef( (float)rot_y + (float)record_y, 1.0, 0.0, 0.0);   //以x軸當旋轉軸
   glRotatef( (float)rot_x + (float)record_x, 0.0, 1.0, 0.0);   //以y軸當旋轉軸
   
   glBindTexture(GL_TEXTURE_2D,textures[0]);//選擇你要用的材質
   glBegin(GL_QUADS);
      glNormal3f(0,0,1);
         glTexCoord2f(0,1);glVertex3f(-5, 5, 5);
         glTexCoord2f(0,0);glVertex3f(-5,-5, 5);
         glTexCoord2f(1,0);glVertex3f( 5,-5, 5);
         glTexCoord2f(1,1);glVertex3f( 5, 5, 5);
   glEnd();

   glBindTexture(GL_TEXTURE_2D,textures[1]);
   glBegin(GL_QUADS);
      glNormal3f(1,0,0);
         glTexCoord2f(0,1);glVertex3f( 5, 5, 5);
         glTexCoord2f(0,0);glVertex3f( 5,-5, 5);
         glTexCoord2f(1,0);glVertex3f( 5,-5,-5);
         glTexCoord2f(1,1);glVertex3f( 5, 5,-5);
   glEnd();
   
   glBindTexture(GL_TEXTURE_2D,textures[2]);
   glBegin(GL_QUADS);
      glNormal3f(0,0,-1);
         glTexCoord2f(0,1);glVertex3f( 5, 5,-5);
         glTexCoord2f(0,0);glVertex3f( 5,-5,-5);
         glTexCoord2f(1,0);glVertex3f(-5,-5,-5);
         glTexCoord2f(1,1);glVertex3f(-5, 5,-5);
   glEnd();
   
   glBindTexture(GL_TEXTURE_2D,textures[3]);
   glBegin(GL_QUADS);
      glNormal3f(-1,0,0);
         glTexCoord2f(0,1);glVertex3f(-5, 5,-5);
         glTexCoord2f(0,0);glVertex3f(-5,-5,-5);
         glTexCoord2f(1,0);glVertex3f(-5,-5, 5);
         glTexCoord2f(1,1);glVertex3f(-5, 5, 5);
   glEnd();

   glBindTexture(GL_TEXTURE_2D,textures[4]);
   glBegin(GL_QUADS);
      glNormal3f(0,1,0);
         glTexCoord2f(0,1);glVertex3f(-5, 5,-5);
         glTexCoord2f(0,0);glVertex3f(-5, 5, 5);
         glTexCoord2f(1,0);glVertex3f( 5, 5, 5);
         glTexCoord2f(1,1);glVertex3f( 5, 5,-5);
   glEnd();

   glBindTexture(GL_TEXTURE_2D,textures[5]);
   glBegin(GL_QUADS);
      glNormal3f(0,-1,0);
         glTexCoord2f(0,1);glVertex3f(-5,-5, 5);
         glTexCoord2f(0,0);glVertex3f(-5,-5,-5);
         glTexCoord2f(1,0);glVertex3f( 5,-5,-5);
         glTexCoord2f(1,1);glVertex3f( 5,-5, 5);
   glEnd();
   glutSwapBuffers();
}

void Keyboard(unsigned char key, int x, int y)
{
   switch (key)
   {
   case 'w':
      distance+=1;
      break;
   case 's':
      distance-=1;
      break;
   case 27:
      glDisable(GL_LIGHT0);
      glDisable(GL_LIGHTING);
      glDisable(GL_DEPTH_TEST);
      glDisable(GL_TEXTURE_2D);
      glDeleteTextures(6,textures);
      glutDestroyWindow(WinNumber);
      exit(0);
      break;
   }
   glutPostRedisplay();   //令視窗重繪
}

void WindowSize(int w, int h)
{
   float rate;
   if( h==0 ) h = 1;                  //阻止h為零,分母可不能為零啊
   glViewport( 0, 0, w, h);            //當視窗長寬改變時,畫面也跟著變
   rate = (float)w/(float)h;            //畫面視野變了,但內容不變形
   
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   gluPerspective( 45, rate, 1.0, 500.0);   //透視投影
   
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
}

void Mouse(int button, int state, int x, int y)
{
   if(state)
   {
      record_x += x - old_rot_x;
      record_y += y - old_rot_y;
      
      rot_x = 0;   //沒有歸零會有不理想的結果
      rot_y = 0;
   }
   else
   {
      old_rot_x = x;
      old_rot_y = y;
   }
}

void MotionMouse(int x, int y)
{
   rot_x = x - old_rot_x;
   rot_y = y - old_rot_y;
   glutPostRedisplay();
}

void SetLightSource()
{
   float light_ambient[]  = { 1.0, 1.0, 1.0, 1.0};
   float light_diffuse[]  = { 1.0, 1.0, 1.0, 1.0};
   float light_specular[] = { 1.0, 1.0, 1.0, 1.0};

   glEnable(GL_LIGHTING);                           //開燈

   // 設定發光體的光源的特性
   glLightfv( GL_LIGHT0, GL_AMBIENT, light_ambient);      //環境光(Ambient Light)
   glLightfv( GL_LIGHT0, GL_DIFFUSE, light_diffuse);      //散射光(Diffuse Light)
   glLightfv( GL_LIGHT0, GL_SPECULAR,light_specular);      //反射光(Specular Light)
   
   glLightfv( GL_LIGHT0, GL_POSITION,light_position);      //光的座標

   glEnable(GL_LIGHT0);                           //開啟零號燈
   glEnable(GL_DEPTH_TEST);
}

void SetMaterial()
{
   float material_ambient[]  = { 0.5, 0.5, 0.5, 1.0};
   float material_diffuse[]  = { 0.1, 0.1, 0.1, 1.0};
   float material_specular[] = { 0.1, 0.1, 0.1, 1.0};

   glMaterialfv( GL_FRONT, GL_AMBIENT,  material_ambient);
   glMaterialfv( GL_FRONT, GL_DIFFUSE,  material_diffuse);
   glMaterialfv( GL_FRONT, GL_SPECULAR, material_specular);
}

void Texture(void)
{
   glGenTextures( 6, textures);
   glPrioritizeTextures( 6, textures, priorities);
   SetTexObj( "bmp\\one.bmp"  ,0);
   SetTexObj( "bmp\\two.bmp"  ,1);
   SetTexObj( "bmp\\three.bmp",2);
   SetTexObj( "bmp\\four.bmp" ,3);
   SetTexObj( "bmp\\five.bmp" ,4);
   SetTexObj( "bmp\\six.bmp"  ,5);
   glEnable(GL_TEXTURE_2D);
}

void SetTexObj(char *name,int i)
{
   glBindTexture(GL_TEXTURE_2D,textures[i]);
   int width;
   int height;
   unsigned char *image;                     //放置圖檔,已經不是BMP圖了,是能直接讓OpenGL使用的資料了
   BITMAPINFO bmpinfo;                        //用來存放HEADER資訊
   
   image = LoadBitmapFile( name, &bmpinfo);
   width = bmpinfo.bmiHeader.biWidth;
   height = bmpinfo.bmiHeader.biHeight;

   //材質控制
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
   
   //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
   //glTexImage2D(GL_TEXTURE_2D,0,3,width,height,0,GL_RGB,GL_UNSIGNED_BYTE,image);
   
   //使用多材質
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
   gluBuild2DMipmaps(GL_TEXTURE_2D,3,width,height,GL_RGB,GL_UNSIGNED_BYTE,image);
}

unsigned char *LoadBitmapFile(char *fileName, BITMAPINFO *bitmapInfo)
{
   FILE            *fp;
   BITMAPFILEHEADER   bitmapFileHeader;   // Bitmap file header
   unsigned char       *bitmapImage;      // Bitmap image data
   unsigned int      lInfoSize;         // Size of information
   unsigned int      lBitSize;         // Size of bitmap
   
   unsigned char change;
    int pixel;
    int p=0;
       
   fp = fopen(fileName, "rb");
   fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1, fp);         //讀取 bitmap header
   
   lInfoSize = bitmapFileHeader.bfOffBits - sizeof(BITMAPFILEHEADER);   //Info的size
   fread(bitmapInfo, lInfoSize, 1, fp);
   
   
   lBitSize = bitmapInfo->bmiHeader.biSizeImage;                  //配置記憶體
   bitmapImage = new BYTE[lBitSize];
   fread(bitmapImage, 1, lBitSize, fp);                        //讀取影像檔
   
   fclose(fp);
   
   //此時傳回bitmapImage的話,顏色會是BGR順序,下面迴圈會改順序為RGB
   pixel = (bitmapInfo->bmiHeader.biWidth)*(bitmapInfo->bmiHeader.biHeight);

   for( int i=0 ; i<pixel ; i++, p+=3 )
   {
      //交換bitmapImage[p]和bitmapImage[p+2]的值
      change = bitmapImage[p];
      bitmapImage[p] = bitmapImage[p+2];
      bitmapImage[p+2]  = change;
   }
   
   return bitmapImage;
}


其中這五張圖只是256*256大小的24bit點陣圖
代碼:

void Texture(void)
{
   glGenTextures( 6, textures);
   glPrioritizeTextures( 6, textures, priorities);
   SetTexObj( "bmp\\one.bmp"  ,0);
   SetTexObj( "bmp\\two.bmp"  ,1);
   SetTexObj( "bmp\\three.bmp",2);
   SetTexObj( "bmp\\four.bmp" ,3);
   SetTexObj( "bmp\\five.bmp" ,4);
   SetTexObj( "bmp\\six.bmp"  ,5);
   glEnable(GL_TEXTURE_2D);
}

然後放在名為"bmp"的資料夾裡面

程式寫成全螢幕模式的,按Esc鍵離開
回頂端
檢視會員個人資料 發送私人訊息 參觀發表人的個人網站
從之前的文章開始顯示:   
發表新主題   回覆主題    電腦遊戲製作開發設計論壇 首頁 -> 遊戲程式高級班:DirectX、OpenGL及各種圖型函式庫 所有的時間均為 台灣時間 (GMT + 8 小時)
1頁(共1頁)

 
前往:  
無法 在這個版面發表文章
無法 在這個版面回覆文章
無法 在這個版面編輯文章
無法 在這個版面刪除文章
無法 在這個版面進行投票
可以 在這個版面附加檔案
可以 在這個版面下載檔案


Powered by phpBB © 2001, 2005 phpBB Group
正體中文語系由 phpbb-tw 維護製作