還是零分 散播福音的祭司
註冊時間: 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鍵離開 |
|