Lesson 15 具有纹理贴图轮廓字体(Texture Mapped Outline Fonts) 在发布了前两篇有关位图字体和轮廓字体教程以后我收到很多邮件很多读者都想知道如何才能给字体赋予纹理贴图你可以使用自动纹理坐标生成器它会为字体上每个多边形生成纹理坐标
个小注释这段代码是专门针对Windows写它使用了Windowswgl来创建字体显然Apple机系统有aglX系统有glx来支持做同样事情不幸是我不能保证这些代码也是容易使用如果哪位有能在屏幕上显示文字且独立于平台代码请告诉我我将重写个有关字体教程
我们将使用第14课代码来创作纹理字体演示如果中哪部分代码有变化我会重写那部分所有代码以便看出我做改动
下面这部分代码类似于第14课代码但是这次我们还要加上stdarg.h头文件
# <windows.h> // Header File For Windows
# <math.h> // Header File For Math Library
# <stdio.h> // Header File For Standard Input/Output
# <gl\\gl.h> // Header File For The OpenGL32 Library
# <gl\\glu.h> // Header File For The GLu32 Library
# <gl\\glaux.h> // Header File For The Glaux Library
HDC hDC=NULL; // Private GDI Device Context
HGLRC hRC=NULL; // Permanent Rendering Context
HWND hWnd=NULL; // Holds Our Window Handle
HINSTANCE hInstance; // Holds The Instance Of The Application
bool keys[256]; // Array Used For The Keyboard Routine
bool active=TRUE; // Window Active Flag Set To TRUE By Default
bool fullscreen=TRUE; // Fullscreen Flag Set To Fullscreen Mode By Default
我们还要添加个叫做texture整型变量它用于保存纹理后面3行是第14课中代码本课不做改动
GLu texture[1]; // _disibledevent=>font = CreateFont( -12, // Height Of Font
0, // Width Of Font
0, // Angle Of Escapement
0, // Orientation Angle
FW_BOLD, // Font Weight
FALSE, // Italic
FALSE, // Underline
FALSE, // Strikeout
这就是有魔力那行!不使用第14课中ANSI_CHARSET我们将使用SYMBOL_CHARSET这会告诉Windows我们创建字体并不是由标准组成典型字体所谓符号字体通常是由些小图片(符号)组成如果你忘了改变这行wingdings,webdings以及你想用其它符号字体就不会工作
SYMBOL_CHARSET, // Character Set Identier
下面几行没有变化
OUT_TT_PRECIS, // Output Precision
CLIP_DEFAULT_PRECIS, // Clipping Precision
ANTIALIASED_QUALITY, // Output Quality
FF_DONTCARE|DEFAULT_PITCH, // Family And Pitch
既然我们已经选择了符号集标识符我们就可以选择wingdings字体了!
\"Wingdings\"); // Font Name
剩下几行代码没有变化
SelectObject(hDC, font); //Selects The Font We Created
wglUseFontOutlines( hDC, // Select The Current DC
0, // Starting Character
255, // Number Of Display Lists To Build
base, // Starting Display Lists
我们允许有更多误差这意味着GL不会严格遵守字体轮廓线如果你把误差设置为0.0f你就会发现严格地在曲面上贴图存在些问题但是如果你允许定误差很多问题都可以避免
0.1f, // Deviation From The True Outlines
下面 3行代码还是相同
0.2f, // Font Thickness In The Z Direction
WGL_FONT_POLYGONS, // Use Polygons, Not Lines
gmf); // Address Of Buffer To Recieve Data
}
在ReSizeGLScene的前我们要加上下面段代码来读取纹理你可能会认得这些前几课中代码我们创建个保存位图地方读取位图告诉Windows生成个纹理并把它保存在texture[0]中
我们创建种细化纹理(mipmapped texture)这样会看起来好些纹理名字叫做lights.bmp
AUX_RGBImageRec *LoadBMP(char *Filename) // Loads A Bitmap Image
{
FILE *File=NULL; // File Handle
(!Filename) // Make Sure A Filename Was Given
{
NULL; // If Not Return NULL
}
File=fopen(Filename,\"r\"); // Check To See If The File Exists
(File) // Does The File Exist?
{
fclose(File); // Close The Handle
auxDIBImageLoad(Filename);// Load The Bitmap And Return A Poer
}
NULL; // If Load Failed Return NULL
}
LoadGLTextures // Load Bitmaps And Convert To Textures
{
Status=FALSE; // Status Indicator
AUX_RGBImageRec *TextureImage[1]; // Create Storage Space For The Texture
mem(TextureImage,0,(void *)*1); // Set The Poer To NULL
(TextureImage[0]=LoadBMP(\"Data/Lights.bmp\")) // Load the Bitmap
{
Status=TRUE; // Set The Status To TRUE
glGenTextures(1, &texture[0]); // Create The Texture
// Build Linear Mipmapped Texture
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);
下面 4行代码将为我们绘制在屏幕上任何物体自动生成纹理坐标glTexGen非常强大而且复杂如果要完全讲清楚它数学原理需要再写篇教程不过你只要知道GL_S和GL_T是纹理坐标就可以了默认状态下它被设置为提取物体此刻在屏幕上x坐标和y坐标并把它们转换为顶点坐标你会发现到物体在z平面没有纹理只显示些斑纹正面和反面都被赋予了纹理这些都是由glTexGen产生(X(GL_S)用于从左到右映射纹理Y(GL_T)用于从上到下映射纹理
GL_TEXTURE_GEN_MODE允许我们选择我们想在S和T纹理坐标上使用纹理映射模式你有3种选择:
GL_EYE_LINEAR - 纹理会固定在屏幕上它永远不会移动物体将被赋予处于它通过地区那块纹理
GL_OBJECT_LINEAR - 这种就是我们使用模式纹理被固定于在屏幕上运动物体上
GL_SPHERE_MAP - 每个人都喜欢创建种有金属质感物体
需要注意是我省略了很多代码我们还需要设置GL_OBJECT_PLANE但是默认参数就是我们想要参数如果你想了解更多那就买本好书或者查阅MSDN
// Texturing Contour Anchored To The Object
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
// Texturing Contour Anchored To The Object
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
glEnable(GL_TEXTURE_GEN_S);
glEnable(GL_TEXTURE_GEN_T);
}
(TextureImage[0]) // If Texture Exists
{
(TextureImage[0]->data) // If Texture Image Exists
{
free(TextureImage[0]->data); // Free The Texture Image Memory
}
free(TextureImage[0]); // Free The Image Structure
}
Status; // Return The Status
}
在InitGL最后有几行新代码BuildFont被放到了读取纹理代码的后glEnable(GL_COLOR_MATERIAL) 这行被删掉了如果你想使用glColor3f(r,g,b)来改变纹理颜色那么就把glEnable(GL_COLOR_MATERIAL)这行重新加到这部分代码中
InitGL(GLvoid) // All Setup For OpenGL Goes Here
{
(!LoadGLTextures) // Jump To Texture Loading Routine
{
FALSE; // If Texture Didn\'t Load Return FALSE
}
BuildFont; // Build The Font
glShadeModel(GL_SMOOTH); // Enable Smooth Shading
glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // Black Background
glClearDepth(1.0f); // Depth Buffer Setup
glEnable(GL_DEPTH_TEST); // Enables Depth Testing
glDepthFunc(GL_LEQUAL); // The Type Of Depth Testing To Do
glEnable(GL_LIGHT0); // Quick And Dirty Lighting (Assumes Light0 Is Set Up)
glEnable(GL_LIGHTING); // Enable Lighting
glH(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Really Nice Perspective Calculations
启动2D纹理映射并选择第个纹理这样就把第个纹理映射到我们绘制在屏幕上3D物体上了如果你想加入更多操作可以按自己意愿启动或禁用纹理映射
glEnable(GL_TEXTURE_2D); // Enable Texture Mapping
glBindTexture(GL_TEXTURE_2D, texture[0]); // Select The Texture
TRUE; // Initialization Went OK
重置大小代码没有变化但DrawGLScene这部分代码有变化
DrawGLScene(GLvoid) // Here\'s Where We Do All The Drawing
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear The Screen And The Depth Buffer
glLoadIdentity; // Re The View
这里是第处变动我们打算使用COS和SIN让物体绕着屏幕旋转而不是把它固定在屏幕中间我们将把物体向屏幕里移动3个单位在x轴我们将移动范围限制在-1.1到+1.1的间我们使用rot变量来控制左右移动我们把上下移动范围限制在+0.8到-0.8的间同样使用rot变量来控制上下移动(最好充分利用你变量)
Position The Texture
glTranslatef(1.1f*float(cos(rot/16.0f)),0.8f*float(sin(rot/20.0f)),-3.0f);
下面做常规旋转这会使符号在XY和Z轴旋转
glRotatef(rot,1.0f,0.0f,0.0f); // Rotate _disibledevent=> TRUE; // Keep Going
}
最后要做事就是在KillGLWindow最后添加KillFont如下所示添加这行代码很重要它将在我们退出的前做清理工作
(!UnregisterClass(\"OpenGL\",hInstance)) // Are We Able To Unregister Class
{
MessageBox(NULL,\"Could Not Unregister Class.\",\"SHUTDOWN ERROR\",MB_OK | MB_ICONINFORMATION);
hInstance=NULL; // Set hInstance To NULL
}
KillFont;
尽管我没有讲细致入微但我想你应该很好理解了如何让OpenGL为你生成纹理坐标在给你字体或者是同类物体赋予纹理映射时应该没有问题了而且只需要改变两行代码你就可以启用球体映射了它效果简直酷毙了!
原著:Jeff Molofee (NeHe) http://nehe.gamedev.net
翻译:史峰 sh[email protected] 山西太原
最新评论