超酷三維漢字特效

  大家好,我是Skyman(SM3D),喜歡三維編程,現在正在系統學習OpenGL,DirectX,VR等。我經常到各大論壇上去轉轉。其中去得最多的地方是NeHe的OpenGL網站(http://nehe.gamedev.net),那是我所見過的最好的學習OpenGL的網站,強烈推薦!!!這是我的第一篇關於OpenGL的文章,寫得不是很好,歡迎大家指正。大家知道,在OpenGL中輸出文本有兩個函數:wglUseFontBitmaps和wglUseFontOutlines,前者用來輸出2維文字,後者用來輸出3維文字。而要輸出漢字,必須使用TrueType字體。本文就教你如何使用wglUseFontOutlines來創建很酷的三維漢字特效。從此文,你可以學到三點知識:

  1. 如何從文件中載入位圖作爲紋理;
  2. 如何生成三維漢字;
  3. 如何將紋理貼到三維漢字上。 

  我使用的是NeHe的Simple框架。爲了簡便起見,我這裏只列出增加的代碼。首先,再頭文件定義區加上: #include <stdio.h>

  接着,在bool fullscreen=TRUE後面定義要用到的變量:

GLuint texture[1];		//紋理ID
int rot=0;			//三維漢字繞x軸旋轉的角度
HFONT hFont;			//字體句柄
LOGFONT lf;			//邏輯字體
GLYPHMETRICSFLOAT gmf[128];	//包括字形的位置和方向信息的結構
unsigned int ichar=0; 		//字符的整型值
char cchar; 			//要轉換爲顯示列表的字符
unsigned int i=0;		//循環變量
unsigned int j=0; 		//循環變量
char Text[128];			//存放要顯示的字符的數組
BYTE TextList[128];		//顯示列表
const GLuint ListBase=1000; 	//顯示列表的基

  然後在LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);後面定義載入文理的函數LoadBMP和LoadGLTextures,這個在NeHe的許多教程裏都有,這裏再寫一下:

AUX_RGBImageRec *LoadBMP(char* filename)            // 載入位圖
{
	FILE* file = NULL;								// 文件句柄
	if (!filename)										//文件名已給了嗎?
	{
		return NULL;									// 若沒有則返回NULL
	}
	file = fopen(filename"r");					// 檢查文件是否存在
	if (file)												// 文件存在嗎?
	{
		fclose(file);									// 關閉文件
		return auxDIBImageLoad(filename);		// 載入位圖並返回位圖的指針
	}
	return NULL;										// 若載入失敗則返回NULL
}
int LoadGLTextures()							// 載入位圖並轉換爲紋理
{
	int status = FALSE;							// 狀態指示
	AUX_RGBImageRec* textureImage[1];				// 爲紋理創建存儲空間
	memset(textureImage, 0, sizeof(void *)*1);		// 清零
	
	// 載入位圖,檢查誤,如位圖不存在則退出
	if (textureImage[0] = LoadBMP("data/tex.bmp"))	//紋理圖片的路徑
	{
		status = TRUE;									// 狀態設爲TRUE
		glGenTextures(1, &texture[0]);		// 創建紋理
		glBindTexture(GL_TEXTURE_2D, texture[0]);
		gluBuild2DMipmaps(GL_TEXTURE_2D, 3, textureImage[0]->sizeX, textureImage[0]->sizeY, GL_RGB, GL_UNSIGNED_BYTE, textureImage[0]->data);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);									//過濾
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);		//過濾
		glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);		//自動生成紋理座標
		glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);		//自動生成紋理座標
		glEnable(GL_TEXTURE_GEN_S);
		glEnable(GL_TEXTURE_GEN_T);
	}
	
	if (textureImage[0])						// 如果紋理存在
	{
		if (textureImage[0]->data)		// 如果紋理圖象存在
		{
			free(textureImage[0]->data);	// 釋放紋理圖象所佔用的內存
		}
		free(textureImage[0]);					// 釋放圖象結構
	}
	return status;								// 返回狀態
}

  註明:因爲三維字體是比較複雜的模型,所以要手工定義紋理座標很難。我們可以讓OpenGL自動生成紋理座標。函數glTexGen()用於自動生成紋理座標。

void gltexgen{ifd}(GLenum coord,GLenum pname,TYPE param);

  參數coord必須是GL_S,GL_T,GL_R或GL_Q,表明是否要生成紋理座標s,t,r或q。參數pname爲GL_TEXTURE_GEN_MODE,表示紋理座標的生成模式。參數param可選GL_OBJECT_LINEAR,GL_EYE_LINEAR或GL_SPHERE_MAP。 GL_OBJECT_LINEAR表示紋理圖象相對於一個移動的物體靜止,本程序就採用這種方式。

  然後在InitGL()函數中的glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);後面加入如下語句:

glEnable(GL_LIGHT0);							// 激活默認光照 (雖然光照效果不是很好但是速度快)
glEnable(GL_LIGHTING);						// 激活光照
glEnable(GL_COLOR_MATERIAL);			// 激活材質顏色
glEnable(GL_TEXTURE_2D);					// 激活二維紋理
glBindTexture(GL_TEXTURE_2D, texture[0]);	// 綁定紋理
memset(&lf, 0, sizeof(LOGFONT));		// 清空內存
//設定邏輯字體lf的屬性
lf.lfHeight = -1;							// 指定字高
lf.lfWidth = -1;							// 指定字寬
lf.lfEscapement = 0;					// 指定角度(1/10度)
lf.lfOrientation = 0;					// 指定字符的基線與x軸的角度(1/10度)
lf.lfWeight = FW_BOLD;			// 指定字體的重量(FW_BOLD=700)
lf.lfItalic = FALSE;						// 指定是否斜體
lf.lfUnderline = FALSE; 				// 指定是否有下劃線
lf.lfStrikeOut = FALSE; 				// 指定是否是StrikeOut字體
lf.lfCharSet = ANSI_CHARSET;	// 指定字符集
lf.lfOutPrecision = OUT_TT_PRECIS;	// 指定輸出精度
lf.lfClipPrecision = CLIP_DEFAULT_PRECIS ;	// 指定裁剪精度
lf.lfQuality = PROOF_QUALITY;		// 指定輸出質量
lf.lfPitchAndFamily = VARIABLE_PITCH | TMPF_TRUETYPE | FF_MODERN ;	// 指定字體的定位和外觀
lstrcpy (lf.lfFaceName, "宋體") ;	// 指定字體名稱
hFont=CreateFontIndirect(&lf);		// 創建邏輯字體
SelectObject(hDC, hFont);				// 將邏輯字體選入設備環境

  然後在DrawGLScene()函數中的glLoadIdentity();後面加入:

strcpy(text, "重慶大學歡迎你");		// 指定所要顯示的字符
i = 0;		// 對i賦初制值
j = 0;		// 對j賦初制值
while (i < 128)
{
	if(IsDBCSLeadByte(text[i]))	// 判斷是否爲雙字節
	{
		ichar = text[i];
		ichar = (ichar << 8) + 256; 	// 256爲漢字內碼的“偏移量”
		ichar = ichar + text[i + 1];
		i++;
		i++;
		wglUseFontOutlines(
			hDC, 	// 設備環境句柄
			ichar,	// 要轉換爲顯示列表的第一個字符
			1, 			// 要轉換爲顯示列表的字符數
			1000 + j,	// 顯示列表的基數
			0.0f, 		// 指定與實際輪廓的最大偏移量
			0.2f, 		// 在Z軸負方向的值
			WGL_FONT_POLYGONS,	// 指定顯示列表用多邊形創建字體
			&gmf[j]
		); 	// 接受字符的地址
		textList[j] = j;
		j++;
	}
	else		//若是單字節(英文)
	{
		cchar = text[i];
		i++;
		wglUseFontOutlines(
			hDC, 	// 設備環境句柄
			cchar, 	// 要轉換爲顯示列表的第一個字符
			1,			// 要轉換爲顯示列表的字符數
			1000 + j, // 顯示列表的基數
			0.0f, 		// 指定與實際輪廓的最大偏移量
			0.5f, 		// 在Z軸負方向的值
			WGL_FONT_POLYGONS, 	// 指定顯示列表用多邊形創建字體
			&gmf[j]
		); 	// 接受字符的地址
		textList[j] = j;
		j++;
	}
}
glListBase(listBase);		// 設置顯示列表的初始索引值
glTranslatef(-4.0f, 0.0f, -10.0f);	// 要顯示的文本的位置
glRotatef(float(rot), 1.0f, 0.0f, 0.0f);	// 文本繞x軸旋轉
glCallLists(strlen(Text),GL_UNSIGNED_BYTE,& TextList);//調用多顯示列表繪製文本
glFlush(); // 強制繪圖
rot = (rot + 1) % 360;	// 旋轉變量遞增

註明:

  1. 雙字節內碼DBCS(雙字節字符集) 支持很多不同的東亞語言字母,如漢語、日語和朝鮮語等。 DBCS 使用數字 0–128 表示 ASCII 字符集。其它大於 128 的數字作爲前導字節字符,它並不是真正的字符,只是簡單的表明下一個字符屬於非拉丁字符集。在 DBCS 中,ASCII 字符的長度是一個字節,而日語、朝鮮語和其它東亞字符的長度是 2 個字節。漢字內碼的偏移量爲256。
  2. 執行多顯示列表可以有效的提高渲染速度。方法是將顯示列表的索引值放入一個數組中,然後調用glCallLists()命令。對於有多種字體的情況,需要爲每種字體建立一個不同的初始顯示列表索引值。這些初始索引值可以在調用glCallLists()函數之前,通過調用glListBase()函數來指定。該函數指定一個偏移量,該偏移量被加到glCallLists()函數中的顯示列表索引中,以獲取最終的顯示列表索引值。函數glCallLists(n,type,*lists)執行n個顯示列表。所執行的列表索引值是由當前顯示列表基(由glListBase()指定)指明的偏移量,加上由 lists指向的數組中的整數之和。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章