大家好,我是Skyman(SM3D),喜欢三维编程,现在正在系统学习OpenGL,DirectX,VR等。我经常到各大论坛上去转转。其中去得最多的地方是NeHe的OpenGL网站(http://nehe.gamedev.net),那是我所见过的最好的学习OpenGL的网站,强烈推荐!!!这是我的第一篇关于OpenGL的文章,写得不是很好,欢迎大家指正。大家知道,在OpenGL中输出文本有两个函数:wglUseFontBitmaps和wglUseFontOutlines,前者用来输出2维文字,后者用来输出3维文字。而要输出汉字,必须使用TrueType字体。本文就教你如何使用wglUseFontOutlines来创建很酷的三维汉字特效。从此文,你可以学到三点知识:
我使用的是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; // 旋转变量递增
注明: