天天看點

openGL 紋理使用

最近找了點資料學習了下openGL 紋理的使用

先有個 整體把握, 然後再去看大部頭中的細節講解, 感覺這樣的學習方式好些

總結下紋理使用總體流程:

1 啟用紋理

glEnable(GL_TEXTURE_2D);

2 載入紋理

3 紋理 的顯示

載入紋理

1 讀取紋理圖像高寬和像素資料到記憶體中,老版本openGL需要考慮寬度和高度不是的整數次方

2 配置設定一個新的紋理編号 glGenTextures(1, &texture_ID);

3 綁定紋理 glBindTexture(GL_TEXTURE_2D, texture_ID);表示目前所使用的紋理對象

4 設定紋理參數, 一堆函數調用glTexParameter*

5 載入 glTexImage2D

紋理的顯示

顯示某個紋理之前先綁定 glBindTexture(GL_TEXTURE_2D, texGround);

指定繪制方法  glBegin(GL_QUADS);

指定紋理坐标和對應頂點 glTexCoord2f(0.0f, 0.0f); glVertex3f(-8.0f, -8.0f, 0.0f);

#define WindowWidth 400
#define WindowHeight 400
#define WindowTitle "OpenGL 紋理測試"
#include <gl/glut.h>
#include <stdio.h>
#include <stdlib.h>
/* 函數 grab
* 抓取視窗中的像素
* 假設視窗寬度為 WindowWidth,高度為 WindowHeight
*/
#define BMP_Header_Length 54
void grab(void)
{
	FILE* pDummyFile;
	FILE* pWritingFile;
	GLubyte* pPixelData;
	GLubyte BMP_Header[BMP_Header_Length];
	GLint i, j;
	GLint PixelDataLength;
	// 計算像素資料的實際長度
	i = WindowWidth * 3; // 得到每一行的像素資料長度
	while( i%4 != 0 ) // 補充資料,直到 i 是的倍數
		++i; // 本來還有更快的算法,
	// 但這裡僅追求直覺,對速度沒有太高要求
	PixelDataLength = i * WindowHeight;
	// 配置設定記憶體和打開檔案
	pPixelData = (GLubyte*)malloc(PixelDataLength);
	if( pPixelData == 0 )
		exit(0);
	pDummyFile = fopen("dummy.bmp", "rb");
	if( pDummyFile == 0 )
		exit(0);
	pWritingFile = fopen("grab.bmp", "wb");
	if( pWritingFile == 0 )
		exit(0);
	// 讀取像素
	glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
	glReadPixels(0, 0, WindowWidth, WindowHeight,
		GL_BGR_EXT, GL_UNSIGNED_BYTE, pPixelData);
	// 把 dummy.bmp 的檔案頭複制為新檔案的檔案頭
	fread(BMP_Header, sizeof(BMP_Header), 1, pDummyFile);
	fwrite(BMP_Header, sizeof(BMP_Header), 1, pWritingFile);
	fseek(pWritingFile, 0x0012, SEEK_SET);
	i = WindowWidth;
	j = WindowHeight;
	fwrite(&i, sizeof(i), 1, pWritingFile);
	fwrite(&j, sizeof(j), 1, pWritingFile);
	// 寫入像素資料
	fseek(pWritingFile, 0, SEEK_END);
	fwrite(pPixelData, PixelDataLength, 1, pWritingFile);
	// 釋放記憶體和關閉檔案
	fclose(pDummyFile);
	fclose(pWritingFile);
	free(pPixelData);
}

/* 函數 power_of_two
* 檢查一個整數是否為 2 的整數次方,如果是,傳回 1,否則傳回 0
* 實際上隻要檢視其二進制位中有多少個,如果正好有 1 個,傳回 1,否則傳回 0
* 在“檢視其二進制位中有多少個”時使用了一個小技巧
* 使用 n &= (n-1)可以使得 n 中的減少一個(具體原理大家可以自己思考)
*/
int power_of_two(int n)
{
	if( n <= 0 )
		return 0;
	return (n & (n-1)) == 0;
}
/* 函數 load_texture
* 讀取一個 BMP 檔案作為紋理
* 如果失敗,傳回 0,如果成功,傳回紋理編号
*/
GLuint load_texture(const char* file_name)
{
	GLint width, height, total_bytes;
	GLubyte* pixels = 0;
	GLuint last_texture_ID, texture_ID = 0;
	// 打開檔案,如果失敗,傳回
	FILE* pFile = fopen(file_name, "rb");
	if( pFile == 0 )
		return 0;
	// 讀取檔案中圖象的寬度和高度
	fseek(pFile, 0x0012, SEEK_SET);
	fread(&width, 4, 1, pFile);
	fread(&height, 4, 1, pFile);
	fseek(pFile, BMP_Header_Length, SEEK_SET);
	// 計算每行像素所占位元組數,并根據此資料計算總像素位元組數
	{
		GLint line_bytes = width * 3;
		while( line_bytes % 4 != 0 )
			++line_bytes;
		total_bytes = line_bytes * height;
	}
	// 根據總像素位元組數配置設定記憶體
	pixels = (GLubyte*)malloc(total_bytes);
	if( pixels == 0 )
	{
		fclose(pFile);
		return 0;
	}
	// 讀取像素資料
	if( fread(pixels, total_bytes, 1, pFile) <= 0 )
	{
		free(pixels);
		fclose(pFile);
		return 0;
	}
	// 在舊版本的 OpenGL 中
	// 如果圖象的寬度和高度不是的整數次方,則需要進行縮放
	// 這裡并沒有檢查 OpenGL 版本,出于對版本相容性的考慮,按舊版本處理
	// 另外,無論是舊版本還是新版本,
	// 當圖象的寬度和高度超過目前 OpenGL 實作所支援的最大值時,也要進行縮放
	{
		GLint max;
		glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max);
		if( !power_of_two(width)
			|| !power_of_two(height)
			|| width > max
			|| height > max )
		{
			const GLint new_width = 256;
			const GLint new_height = 256; // 規定縮放後新的大小為邊長的正方形
			GLint new_line_bytes, new_total_bytes;
			GLubyte* new_pixels = 0;
			// 計算每行需要的位元組數和總位元組數
			new_line_bytes = new_width * 3;
			while( new_line_bytes % 4 != 0 )
				++new_line_bytes;
			new_total_bytes = new_line_bytes * new_height;
			// 配置設定記憶體
			new_pixels = (GLubyte*)malloc(new_total_bytes);
			if( new_pixels == 0 )
			{
				free(pixels);
				fclose(pFile);
				return 0;
			}
			// 進行像素縮放
			gluScaleImage(GL_RGB,
				width, height, GL_UNSIGNED_BYTE, pixels,
				new_width, new_height, GL_UNSIGNED_BYTE, new_pixels);
			// 釋放原來的像素資料,把 pixels 指向新的像素資料,并重新設定 width 和 height
			free(pixels);
			pixels = new_pixels;
			width = new_width;
			height = new_height;
		}
	}
	// 配置設定一個新的紋理編号
	glGenTextures(1, &texture_ID);
	if( texture_ID == 0 )
	{
		free(pixels);
		fclose(pFile);
		return 0;
	}
	// 綁定新的紋理,載入紋理并設定紋理參數
	// 在綁定前,先獲得原來綁定的紋理編号,以便在最後進行恢複
	glGetIntegerv(GL_TEXTURE_BINDING_2D,(GLint *) &last_texture_ID);
	glBindTexture(GL_TEXTURE_2D, texture_ID);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0,
		GL_BGR_EXT, GL_UNSIGNED_BYTE, pixels);
	glBindTexture(GL_TEXTURE_2D, last_texture_ID);
	// 之前為 pixels 配置設定的記憶體可在使用 glTexImage2D 以後釋放
	// 因為此時像素資料已經被 OpenGL 另行儲存了一份(可能被儲存到專門的圖形硬體中)
	free(pixels);
	return texture_ID;
}

/* 兩個紋理對象的編号
*/
GLuint texGround;
GLuint texWall;
void display(void)
{
	// 清除螢幕
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	// 設定視角
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(75, 1, 1, 21);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(1, 5, 5, 0, 0, 0, 0, 0, 1);
	// 使用“地”紋理繪制土地
	// 土地在Z平面上
	glBindTexture(GL_TEXTURE_2D, texGround);
	glBegin(GL_QUADS);
	glTexCoord2f(0.0f, 0.0f); glVertex3f(-8.0f, -8.0f, 0.0f);
	glTexCoord2f(0.0f, 5.0f); glVertex3f(-8.0f, 8.0f, 0.0f);
	glTexCoord2f(5.0f, 5.0f); glVertex3f(8.0f, 8.0f, 0.0f);
	glTexCoord2f(5.0f, 0.0f); glVertex3f(8.0f, -8.0f, 0.0f);
	glEnd();
	// 使用“牆”紋理繪制栅欄
	glBindTexture(GL_TEXTURE_2D, texWall);
	glBegin(GL_QUADS);
	glTexCoord2f(0.0f, 0.0f); glVertex3f(-6.0f, -3.0f, 0.0f);
	glTexCoord2f(0.0f, 1.0f); glVertex3f(-6.0f, -3.0f, 1.5f);
	glTexCoord2f(5.0f, 1.0f); glVertex3f(6.0f, -3.0f, 1.5f);
	glTexCoord2f(5.0f, 0.0f); glVertex3f(6.0f, -3.0f, 0.0f);
	glEnd();
	// 旋轉後再繪制一個
	glRotatef(-90, 0, 0, 1);
	glBegin(GL_QUADS);
	glTexCoord2f(0.0f, 0.0f); glVertex3f(-6.0f, -3.0f, 0.0f);
	glTexCoord2f(0.0f, 1.0f); glVertex3f(-6.0f, -3.0f, 1.5f);
	glTexCoord2f(5.0f, 1.0f); glVertex3f(6.0f, -3.0f, 1.5f);
	glTexCoord2f(5.0f, 0.0f); glVertex3f(6.0f, -3.0f, 0.0f);
	glEnd();
	// 交換緩沖區,并儲存像素資料到檔案
	glutSwapBuffers();
	grab();
}
int main(int argc, char* argv[])
{
	// GLUT 初始化
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
	glutInitWindowPosition(100, 100);
	glutInitWindowSize(WindowWidth, WindowHeight);
	glutCreateWindow(WindowTitle);
	glutDisplayFunc(&display);
	// 在這裡做一些初始化
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_TEXTURE_2D);
	texGround = load_texture("ground.bmp");
	texWall = load_texture("wall.bmp");
	// 開始顯示
	glutMainLoop();
	return 0;
}
           

代碼下載下傳

打包下載下傳

繼續閱讀