天天看點

Open gl ——光照材質紋理的綜合使用

操作方法:按滑鼠左鍵,繞x軸旋轉值+=12角度

                  按滑鼠中鍵,繞y軸旋轉值+=12角度

                  按滑鼠右鍵,繞z軸旋轉值+=12角度

                  按鍵盤x,eye_x--      按鍵盤X, eye_x++

                  按鍵盤y,eye_y--      按鍵盤Y, eye_y++

                  按鍵盤z,eye_z--      按鍵盤Z ,eye_z++

                  按a或A,切換使用材質與否

                  按s或S,切換使用紋理與否

其實一直想做個arcBall操作方式,可惜線性代數學的不好,又缺乏耐心去搞懂如何把多元矩陣的運算映射成通過代碼的來運算的方法。不然真的很爽,隻要拖拖拽拽,就能任意觀察模型了。

// 此代碼加載大女孩luweiqi.txt資料檔案
#include <stdio.h>     
#include "glut.h"   
#include <gl/glaux.h>
#pragma comment(lib, "openGL32.lib")
#pragma comment(lib, "glu32.lib")
#pragma comment(lib, "glaux.lib")

int countOfTextureFile;     // 紋理檔案數量
int countOfMaterial;        // 材質數量
int countOfVertex;          // 頂點數量
int countOfTexturePosition; // 貼圖坐标數量
int countOfNormal;          // 法線數量
int countOfSubModelNum;     // 模型分組數量

char**   texture_file_list; // 記住不能string* texture_file_list;
AUX_RGBImageRec **TextureImage;
GLuint*  texture;           // 存儲多個紋理

struct material             // 建立結構體,可以避免發雜性,以及可以減少動态生成數組的維數
{
	float ambient[4];
	float diffuse[4];
	float specular[4];
	float emission[4];
    float shiness;
    float indexOfTextureFile; // 這個變量怎麼用??????????
};
material* material_list;

struct vertex   // 建立結構體,可以避免發雜性,以及可以減少動态生成數組的維數,減少複雜性
{
	float x;
	float y;
	float z;
};
vertex* vertex_list;

struct texture_position     // 每個貼圖的坐标
{
	float x;
	float y;
};
texture_position* texture_position_list;

struct normal
{
	float x;
	float y;
	float z;
};
normal* normal_list;
struct scale_factor         // 縮放系數 
{
	float x;
	float y;
	float z;
};
scale_factor g_scale_factor; 

struct triangle
{
	int vi1;              // 頂點1位置索引指向頂點編号
	int ti1;              // 頂點1的貼圖坐标編号 
	int ni1;              // 頂點1的法線編号
	
	int vi2;
	int ti2;
	int ni2;
	
	int vi3;
	int ti3;
	int ni3;
};

struct submodel
{ 
	int countOfTriangle;      // the submode 的三角形數量
	int indexOfMaterial;      // the submode 的材質索引
    triangle* triangle_list;  // the submode 的所有三角形list
};
submodel* submodel_list;


//int g_iSpinX = 0;
//int g_iSpinY = 0;
//int g_iSpinZ = 0;

//int g_lastMousePosX = 0;
//int g_lastMousePosY = 0;


GLenum  g_mode = GL_TRIANGLES;  //  GL_TRIANGLES 按住t 或T  , GL_LINE_LOOP----按key L 或 l  ,  GL_POINT---按key p 或 P
bool  g_isMaterial = true;
bool  g_isTexture = true;
static GLfloat theta[] = {0.0,0.0,0.0};    //  初始的旋轉角度為0               
static GLint axis = 2;  
static GLdouble viewer[]= {0.0, -15.0, 5.0}; /* initial viewer location */   



// ----------------------載入位圖圖象------------------------
AUX_RGBImageRec *LoadBMP(const CHAR *Filename)
{
	FILE *File=NULL;          // 檔案句柄
	if (!Filename)            // 確定檔案名已提供
	{
		return NULL;          // 如果沒提供,傳回 NULL
	}
	File=fopen(Filename,"r"); // 嘗試打開檔案
	if (File)                 // 檔案存在麼?
	{
		fclose(File);         // 關閉句柄
		return auxDIBImageLoadA(Filename);    // 載入位圖并傳回指針
	}
	return NULL;              // 如果載入失敗,傳回 NULL
}

// ----------------載入位圖(調用上面的代碼)并轉換成紋理,注意先調用readData後調用此函數------------------
int LoadGLTextures(char** fileNameList,int texturefileNameCount)           
{
	int Status=FALSE;                         // 狀态訓示器
	TextureImage=new AUX_RGBImageRec *[texturefileNameCount];         // 建立紋理的存儲空間
	memset(TextureImage,0,sizeof(void *)*texturefileNameCount);       // 将指針設為 NULL
	// 載入位圖,檢查有無錯誤,如果位圖沒找到則退出
    
	for (int i=0;i<texturefileNameCount;i++)
	{
		if ( TextureImage[i]=LoadBMP(fileNameList[i] ) )
		{
			Status=TRUE;                          // 将 Status 設為 TRUE
			glGenTextures(1, &texture[i]);        // 每次建立一個紋理,當然也是可以一次建立texturefileNameCount個,儲存到&texture裡
		}
	/*	if (TextureImage[i])                      // 紋理是否存在
		{
			if (TextureImage[i]->data)            // 紋理圖像是否存在
			{
				free(TextureImage[i]->data);      // 釋放紋理圖像占用的記憶體
			} 
			free(TextureImage[i]);                // 釋放圖像結構
		}*/
	}
	
	return Status;                                // 傳回 Status
}


// -----------------從luweiqi.txt資料檔案讀取資料------------------------   
void readData()  
{ 
	int i;
    FILE *fp;
    if((fp=fopen("luweiqi.txt","r"))==NULL)
    {  
        printf("Can't open the file!\n");   
        exit(0);  
    }  
	1.讀取所有的紋理檔案名字/
    fscanf(fp,"%d",&countOfTextureFile);
	// texture_file_list=new string[countOfTextureFile];
	texture_file_list=new char* [countOfTextureFile] ;
    for (i=0;i<countOfTextureFile;i++)
    {
		texture_file_list[i]=new char[255];
    }
	texture=new GLuint[countOfTextureFile];                     // 2013年12月12日14:25:51
    
    for (i=0;i<countOfTextureFile;i++)
    {
		fscanf(fp,"%s",texture_file_list[i]);                   // 這裡特别容易出錯哦fscanf(fp,"%s",&texture_file_list[i]); 是錯的  
		texture[i]=i;                                           // 2013年12月12日14:25:51
    }
	/2.讀取所有材質資料///
	fscanf(fp,"%d",&countOfMaterial);
    material_list=new material[countOfMaterial];
    for (i=0;i<countOfMaterial;i++)
    {
		// 讀材質的ambient環境光屬性資料
		fscanf(fp,"%f%f%f%f",&material_list[i].ambient[0],
			&material_list[i].ambient[1],
			&material_list[i].ambient[2],
			&material_list[i].ambient[3]);
        // 讀取材質的diffuse漫反射屬性資料
        fscanf(fp,"%f%f%f%f",&material_list[i].diffuse[0],
			&material_list[i].diffuse[1],
			&material_list[i].diffuse[2],
			&material_list[i].diffuse[3]);
		// 讀取材質的specular鏡面反射屬性資料
		fscanf(fp,"%f%f%f%f",&material_list[i].specular[0],
			&material_list[i].specular[1],
			&material_list[i].specular[2],
			&material_list[i].specular[3]);
		// 讀取材質的emission輻射屬性資料
        fscanf(fp,"%f%f%f%f",&material_list[i].emission[0],
			&material_list[i].emission[1],
			&material_list[i].emission[2],
			&material_list[i].emission[3]);
        // 讀取材質的光斑的大小屬性的資料
        fscanf(fp,"%f",&material_list[i].shiness);
        // 讀取材質的indexOfTextureFile所用紋理檔案編号
        fscanf(fp,"%f",&material_list[i].indexOfTextureFile);
    }
    /3.讀取所有頂點的資料//
    fscanf(fp,"%d",&countOfVertex); 
    vertex_list=new vertex[countOfVertex];
	for (i=0;i<countOfVertex;i++)
	{
		fscanf(fp,"%f%f%f",&vertex_list[i].x,&vertex_list[i].y,&vertex_list[i].z);
	}
    /4.讀取所有貼圖坐标///
    fscanf(fp,"%d",&countOfTexturePosition);
    texture_position_list=new texture_position[countOfTexturePosition];
	for (i=0;i<countOfTexturePosition;i++)
	{
		fscanf(fp,"%f%f",&texture_position_list[i].x,&texture_position_list[i].y);
	}
    5.讀取所有法線向量/       沒用上。。。。。
    fscanf(fp,"%d",&countOfNormal);
    normal_list=new normal[countOfNormal];
	for (i=0;i<countOfNormal;i++)
	{
        fscanf(fp,"%f%f%f",&normal_list[i].x,&normal_list[i].y,&normal_list[i].z);
	}
    6.讀取所有子模型的所有三角形/
	fscanf(fp,"%d",&countOfSubModelNum);  
    fscanf(fp,"%f%f%f",&g_scale_factor.x,&g_scale_factor.y,&g_scale_factor.z);
	
	submodel_list=new submodel[countOfSubModelNum];
	
    for (i=0;i<countOfSubModelNum;i++)
    {
		fscanf(fp,"%d",&submodel_list[i].countOfTriangle);
		fscanf(fp,"%d",&submodel_list[i].indexOfMaterial);
		
		// 因為都是unsigned 類型,是以可以使用動态産生的多元數組也行,當然,一維的結構體也行。
		// 但是如果使用前者的方法的話就忽視了這些同類型的不同的變量的作用了,不利于維護,是以
		// 還是選擇動态一維結構體數組的方式
		submodel_list[i].triangle_list=new triangle[submodel_list[i].countOfTriangle];
		for(int j=0;j<submodel_list[i].countOfTriangle;j++)
		{  
			fscanf(fp,"%d%d%d%d%d%d%d%d%d",&submodel_list[i].triangle_list[j].vi1,
										   &submodel_list[i].triangle_list[j].ti1,
									       &submodel_list[i].triangle_list[j].ni1,
										   &submodel_list[i].triangle_list[j].vi2,
										   &submodel_list[i].triangle_list[j].ti2,
									       &submodel_list[i].triangle_list[j].ni2,
										   &submodel_list[i].triangle_list[j].vi3,
										   &submodel_list[i].triangle_list[j].ti3,
										   &submodel_list[i].triangle_list[j].ni3);
		}  
    }
    fclose(fp);  
}  



void display(void);

// 初始化
void init (void) 
{
    // printf operate tip
    printf("GL_TRIANGLES 按住t 或T,GL_LINE_LOOP----按key L 或 l,GL_POINT---按key p 或 P\n");// //  GL_TRIANGLES 按住t 或T  , GL_LINE_LOOP----按key L 或 l  ,  GL_POINT---按key p 或 P
	printf("按滑鼠左鍵,繞x軸旋轉值+=12角度\n");
		
    printf("按滑鼠中鍵,繞y軸旋轉值+=12角度\n");
		
	printf("按滑鼠右鍵,繞z軸旋轉值+=12角度\n");	
		
	printf("按鍵盤x,eye_x--      按鍵盤X, eye_x++\n");	
		
	printf("按鍵盤y,eye_y--      按鍵盤Y, eye_y++\n");	
		
	printf("按鍵盤z,eye_z--      按鍵盤Z ,eye_z++\n");	

	printf("按a 或A 切換是否使用材質\n");

	printf("按s 或S 切換是否使用紋理\n");

	printf("\n");

    glClearColor (0.0, 0.0, 0.0, 0.0);            // 清理顔色,為黑色,(也可認為是背景顔色)
    glEnable(GL_CULL_FACE);                       // 啟用裁剪
    glEnable(GL_TEXTURE_2D);
//	glEnable(GL_MULTISAMPLE);
	glEnable(GL_DEPTH_TEST);

	readData();                                   // 讀入模型資料
    LoadGLTextures(texture_file_list,countOfTextureFile);// 載入所有的紋理貼圖  2013年12月12日13:47:22

//  這裡可以自行打開光照 (和資料檔案無關)
	glEnable( GL_LIGHTING );
    glEnable( GL_LIGHT0 );
	
    float fAmbientColor[] = { 1.0f, 1.0f, 1.0f, 1.0f};   
    glLightfv( GL_LIGHT0, GL_AMBIENT, fAmbientColor );   
	
    float fDiffuseColor[] = { 1.0f, 1.0f, 1.0f, 1.0f };   
    glLightfv( GL_LIGHT0, GL_DIFFUSE, fDiffuseColor );   
	
    float fSpecularColor[] = { 0.0f, 0.0f, 0.0f, 1.0f };   
    glLightfv( GL_LIGHT0, GL_SPECULAR, fSpecularColor );   
	
    float fPosition[] = { 1.0f, 1.0f, 1.0f, 0.0f };   
    glLightfv( GL_LIGHT0, GL_POSITION, fPosition );   
	
    GLfloat ambient_lightModel[] = { 1.0f, 1.0f, 1.0f, 1.0f };   
    glLightModelfv( GL_LIGHT_MODEL_AMBIENT, ambient_lightModel );   


	display();
}

void display(void)  
{  
    int i,j,k;  
	
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  
    glMatrixMode(GL_MODELVIEW);
	
    glLoadIdentity();
	
    gluLookAt(viewer[0],viewer[1],viewer[2], 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
	
  
	// 畫出3個坐标軸
	glBegin(GL_LINES);
	glColor3f(1.0f, 0.0f, 0.0f);
    glVertex3f(0.0f, 0.0f, 0.0f);
	glVertex3f(10.0f, 0.0f, 0.0f);

	glColor3f(0.0f, 1.0f, 0.0f);
    glVertex3f(0.0f, 0.0f, 0.0f);
	glVertex3f(0.0f, 10.0f, 0.0f);
 
	glColor3f(0.0f, 0.0f, 1.0f);
	glVertex3f(0.0f, 0.0f, 0.0f);
	glVertex3f(0.0f, 0.0f, 10.0f);

	glEnd();
	
	
	glRotatef(theta[0], 1.0, 0.0, 0.0);    
    glRotatef(theta[1], 0.0, 1.0, 0.0);  
    glRotatef(theta[2], 0.0, 0.0, 1.0);    

//	glRotatef( -g_iSpinY*0.1, 1.0f, 0.0f, 0.0f );
 //   glRotatef( -g_iSpinX*0.1, 0.0f, 1.0f, 0.0f );
//    glRotatef( -g_iSpinZ*0.1, 0.0f, 0.0f, 1.0f );    // 如果不用複雜的arcBall來操作,似乎無法改變繞z軸旋轉。

    for (i=0,k=0;i<countOfSubModelNum;i++)                 // 子模型數量
    {
		// 1.打開材質
		if (g_isMaterial)
		{	
			glMaterialfv(GL_FRONT, GL_AMBIENT,   material_list[i].ambient);
			glMaterialfv(GL_FRONT, GL_DIFFUSE,   material_list[i].diffuse);
			glMaterialfv(GL_FRONT, GL_SPECULAR,  material_list[i].specular);
			glMaterialfv(GL_FRONT, GL_EMISSION,  material_list[i].emission);
			glMaterialf (GL_FRONT, GL_SHININESS, material_list[i].shiness);
		}


		// 2.打開紋理
		if (g_isTexture)
		{
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); // 線形濾波
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); // 線形濾波
		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);     // 2013年12月13日10:52:55
	  
		int index = material_list[i].indexOfTextureFile -1 ;
		glBindTexture(GL_TEXTURE_2D, texture[ index ]);
	    
		glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[
			index]->sizeX, 
			TextureImage[ index]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, 
			TextureImage[index]->data);
		}
	
		// 3.畫出三角形
		for (j=0;j<submodel_list[i].countOfTriangle;j++)    // 每個子模型的三角形數
		{
			glBegin(g_mode);
			glTexCoord2f(texture_position_list[  submodel_list[i].triangle_list[j].ti1  -1].x, 
				texture_position_list[submodel_list[i].triangle_list[j].ti1 -1].y);
			glVertex3fv((float*)&vertex_list[ submodel_list[i].triangle_list[j].vi1 -1] );
			
			glTexCoord2f(texture_position_list[submodel_list[i].triangle_list[j].ti2 -1 ].x, 
				texture_position_list[submodel_list[i].triangle_list[j].ti2 -1 ].y );
			glVertex3fv((float*)&vertex_list[ submodel_list[i].triangle_list[j].vi2 -1] );
			
			glTexCoord2f(texture_position_list[submodel_list[i].triangle_list[j].ti3 -1].x,
				texture_position_list[submodel_list[i].triangle_list[j].ti3 -1 ].y);
			glVertex3fv((float*)&vertex_list[ submodel_list[i].triangle_list[j].vi3 -1] );
			
            glEnd();
		}
    }
	glFlush();
	
	glutSwapBuffers();
}  

void mouse(int btn, int state, int x, int y)          // 滑鼠左中右鍵改變的是各個方向的旋轉角度    
{  
    if(btn==GLUT_LEFT_BUTTON && state == GLUT_DOWN) axis = 0;
    if(btn==GLUT_MIDDLE_BUTTON && state == GLUT_DOWN) axis = 1;
    if(btn==GLUT_RIGHT_BUTTON && state == GLUT_DOWN) axis = 2;
  
	theta[axis] += 12.0;
    if( theta[axis] > 360.0 ) theta[axis] -= 360.0;

/*	if( btn == GLUT_LEFT_BUTTON && state == GLUT_DOWN)  // GLUT_DOWN 是按下的狀态
	{   	
        g_iSpinX = x - g_lastMousePosX;
		g_iSpinY = y - g_lastMousePosY;

		g_lastMousePosX = x;
		g_lastMousePosY = y;
	}*/

    display();  
}  

void keys(unsigned char key, int x, int y)  
{  
	/* Use x, X, y, Y, z, and Z keys to move viewer */   // 鍵盤的xyz 改變眼睛的位置  viewer[0] viewer[1] viewer[2] 用在gluLookAt   
	
	if(key == 'x') viewer[0]-= 1.0;
	if(key == 'X') viewer[0]+= 1.0;
	if(key == 'y') viewer[1]-= 1.0;
	if(key == 'Y') viewer[1]+= 1.0;
	if(key == 'z') viewer[2]-= 1.0;
	if(key == 'Z') viewer[2]+= 1.0;     
	
//	GLenum  mode = GL_TRIANGLES;  //  GL_TRIANGLES 按住t 或T  , GL_LINE_LOOP----按key L 或 l  ,  GL_POINT---按key p 或 P
    if (key == 't' || key == 'T')
    {
		g_mode = GL_TRIANGLES; 
    }
    if (key == 'l' || key == 'L')
    {
		g_mode = GL_LINE_LOOP;
    }
    if (key == 'p' || key == 'P')
    {
		g_mode = GL_POINTS;
    }

	if (key == 'a'||key == 'A')     // 是否用材質
	{
        g_isMaterial = !g_isMaterial;
	}
    if (key == 's'||key == 'S')     // 是否用紋理
    {
		g_isTexture = !g_isTexture;
    }

	display();
}  

void myReshape(int w, int h)  
{  
	glViewport(0, 0, w, h);  
	/* Use a perspective view */  
	glMatrixMode(GL_PROJECTION);   
	glLoadIdentity();  
	if(w<=h) glFrustum(-0.2, 0.2, -0.2 * (GLfloat) h/ (GLfloat) w,               // 此時h/w >1   -0.2 ~ 0.2     
		0.2* (GLfloat) h / (GLfloat) w, 2.0, 40.0);  
	else glFrustum(-0.2, 0.2, -0.2 * (GLfloat) w/ (GLfloat) h,                   // 此時w/h >1   
		0.2* (GLfloat) w / (GLfloat) h, 2.0, 20.0);  
	
	/* Or we can use gluPerspective */  
//	 gluPerspective(20.0, w/h, -0.1, 0.1);   
	
	glMatrixMode(GL_MODELVIEW);        // 設定回模型矩陣
}  

void  
main(int argc, char **argv)  
{  
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
	glutInitWindowSize(500, 500);
	glutCreateWindow("大女孩");
	
	init();
	
	glutReshapeFunc(myReshape);
	
	glutDisplayFunc(display);
	glutMouseFunc(mouse);
	glutKeyboardFunc(keys);
	
	glutMainLoop();
	
}  
           
Open gl ——光照材質紋理的綜合使用

                                                       操作方式

Open gl ——光照材質紋理的綜合使用

           截圖1 在填充模式下,打開紋理和材質 按t或T 是三角形就是填充模式

Open gl ——光照材質紋理的綜合使用

                        截圖2 在畫線模式下,打開紋理和材質按l 或L

Open gl ——光照材質紋理的綜合使用

                  截圖3 在點模式下,打開紋理和貼圖 (操作是按p或P)

Open gl ——光照材質紋理的綜合使用

           截圖4 在填充模式下,打開材質,關閉紋理(操作時按s進行切換)

Open gl ——光照材質紋理的綜合使用

                                                                                                 同時運作3次exe,來觀察

期間有個很有意思的bug,萬萬沒想到的bug,哈哈。

讀取檔案的函數是我一口氣寫出來的,照着檔案格式寫。運作後發現貼紋理時候會貼裂掉,原因怎麼也找不到,一直檢查讀取資料函數都沒找到原因。問題出在下面的代碼裡的每個頂點的索引,紋理的索引需要額外的減掉1.因為資料檔案裡的索引最小的是1.也就是說生成模型的頂點的索引,紋理的索引是從1開始數的,而我們的讀資料的時候卻是把第一條資料放進了第0号索引,結果總是差1

是以說以後讀資料不能隻看檔案格式,檔案裡的資料也要稍微注意下。這就是我犯的錯誤。

// 3.畫出三角形
		for (j=0;j<submodel_list[i].countOfTriangle;j++)    // 每個子模型的三角形數
		{
			glBegin(GL_TRIANGLES);
			//	glBegin(GL_LINE_LOOP);
			glTexCoord2f(texture_position_list[  submodel_list[i].triangle_list[j].ti1  -1].x, 
				texture_position_list[submodel_list[i].triangle_list[j].ti1 -1].y);
			glVertex3fv((float*)&vertex_list[ submodel_list[i].triangle_list[j].vi1 -1] );
			
			glTexCoord2f(texture_position_list[submodel_list[i].triangle_list[j].ti2 -1 ].x, 
				texture_position_list[submodel_list[i].triangle_list[j].ti2 -1 ].y );
			glVertex3fv((float*)&vertex_list[ submodel_list[i].triangle_list[j].vi2 -1] );
			
			glTexCoord2f(texture_position_list[submodel_list[i].triangle_list[j].ti3 -1].x,
				texture_position_list[submodel_list[i].triangle_list[j].ti3 -1 ].y);
			glVertex3fv((float*)&vertex_list[ submodel_list[i].triangle_list[j].vi3 -1] );
			
            glEnd();
		}
           

是個聰明的女同學幫我找出的,真正體會到了獨學而無友,則孤陋寡聞這句話了。

繼續閱讀