转自【翻译】NeHe OpenGL 教程

前言

声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改。对NeHe的OpenGL管线教程的编写,以及yarn的翻译整理表示感谢。

NeHe OpenGL第十五课:纹理图形字

图形字体的纹理映射:

这一课,我们将在上一课的基础上创建带有纹理的字体,它真的很简单。

在发布了前两篇关于位图字体和轮廓字体的教程以后,我收到很多邮件,很多读者都想知道如何才能给字体赋予纹理贴图。你可以使用自动纹理坐标生成器。它会为字体上的每一个多边形生成纹理坐标。

一个小注释,这段代码是专门针对Windows写的,它使用了Windows的wgl函数来创建字体,显然,Apple机系统有agl,X系统有glx来支持做同样事情的,不幸的是,我不能保证这些代码也是容易使用的。如果哪位

有能在屏幕上显示文字且独立于平台的代码,请告诉我,我将重写一个有关字体的教程。

我们将使用第14课的代码来创作纹理字体的演示。如果程序中哪部分的代码有变化,我会重写那部分的所有代码以便看出我做的改动。

我们还要添加一个叫做texture[]的整型变量。它用于保存纹理。后面3行是第14课中的代码,本课不做改动。

下面的部分做了一些小改动。我打算在这课使用wingdings字体来显示一个海盗旗(骷髅头和十字骨头)的标志。如果你想显示文字的话,就不用改动第14课中的代码了,也可以选择另一种字体。

有些人想知道如何使用wingdings字体,这也是我不用标准字体的一个原因。wingdings是一种符号字体,使用它时需要做一些改动。告诉Windows使用wingdings字体并不太简单。如果你把字体的名字改为

wingdings,你会注意到字体其实并没有选到。你必须告诉Windows这种字体是一种符号字体而不是一种标准字符字体。后面会继续解释。

GLvoid BuildFont(GLvoid)      // 创建位图字体

{

 GLYPHMETRICSFLOAT gmf[256];     // 记录256个字符的信息

 HFONT font;      // 字体句柄

base = glGenLists(256);     // 创建256个显示列表

 font = CreateFont( -12,     // 字体高度

    0,    // 字体宽度

    0,    // 字体的旋转角度 Angle Of Escapement

    0,    // 字体底线的旋转角度Orientation Angle

    FW_BOLD,    // 字体的重量

    FALSE,    // 是否使用斜体

    FALSE,    // 是否使用下划线

    FALSE,    // 是否使用删除线

这就是有魔力的那一行!不使用第14课中的ANSI_CHARSET,我们将使用SYMBOL_CHARSET。这会告诉Windows我们创建的字体并不是由标准字符组成的典型字体。所谓符号字体通常是由一些小图片(符号)组成的

。如果你忘了改变这行,wingdings,webdings以及你想用的其它符号字体就不会工作。

SYMBOL_CHARSET,   // 设置字符集

下面几行没有变化。

OUT_TT_PRECIS,   // 输出精度

    CLIP_DEFAULT_PRECIS,  // 裁剪精度

    ANTIALIASED_QUALITY,  // 输出质量

    FF_DONTCARE|DEFAULT_PITCH,  // Family And Pitch

既然我们已经选择了符号字符集标识符,我们就可以选择wingdings字体了

"Wingdings");   // 字体名称

剩下几行代码没有变化

SelectObject(hDC, font);     // 选择创建的字体

wglUseFontOutlines( hDC,    // 设置当前窗口设备描述表的句柄

    0,    // 用于创建显示列表字体的第一个字符的ASCII值

    255,    // 字符数

    base,    // 第一个显示列表的名称

我们允许有更多的误差,这意味着GL不会严格的遵守字体的轮廓线。如果你把误差设置为0.0f,你就会发现严格地在曲面上贴图存在一些问题。但是如果你允许一定的误差,很多问题都可以避免。

0.1f,    // 字体的光滑度,越小越光滑,0.0为最光滑的状态

下面三行代码还是相同的

0.2f,    // 在z方向突出的距离

    WGL_FONT_POLYGONS,   // 使用多边形来生成字符,每个顶点具有独立的法线

    gmf);    // 一个接收字形度量数据的数组的地址,每个数组元素用它对应的显示列表字符的数据填充

}

在ReSizeGLScene()函数之前,我们要加上下面一段代码来读取纹理。你可能会认得这些前几课中的代码。我们创建一个保存位图的地方,读取位图,告诉Windows生成一个纹理,并把它保存在texture[0]中。

我们创建一种细化纹理(mipmapped texture),这样会看起来好些。纹理的名字叫做lights.bmp。

if (TextureImage[0]=LoadBMP("Data/Lights.bmp"))   // 载入位图

下面四行代码将为我们绘制在屏幕上的任何物体自动生成纹理坐标。函数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 - 每个人都喜欢。创建一种有金属质感的物体。

  

  // 设置纹理映射模式

  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);  

  

在InitGL()的最后有几行新代码。BuildFont()被放到了读取纹理的代码之后。glEnable(GL_COLOR_MATERIAL) 这行被删掉了,如果你想使用glColor3f(r,g,b)来改变纹理的颜色,那么就把glEnable

(GL_COLOR_MATERIAL)这行重新加到这部分代码中。

int InitGL(GLvoid)       // 此处开始对OpenGL进行所有设置

{

 if (!LoadGLTextures())     // 载入纹理

 {

  return FALSE;     // 失败则返回

 }

 BuildFont();      // 创建字体显示列表

glShadeModel(GL_SMOOTH);     // 启用阴影平滑

 glClearColor(0.0f, 0.0f, 0.0f, 0.5f);   // 黑色背景

 glClearDepth(1.0f);     // 设置深度缓存

 glEnable(GL_DEPTH_TEST);     // 启用深度测试

 glDepthFunc(GL_LEQUAL);     // 所作深度测试的类型

 glEnable(GL_LIGHT0);     // 使用第0号灯

 glEnable(GL_LIGHTING);     // 使用光照

 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);  // 告诉系统对透视进行修正

启动2D纹理映射,并选择第一个纹理。这样就把第一个纹理映射到我们绘制在屏幕上的3D物体上了。如果你想加入更多的操作,可以按自己的意愿启动或禁用纹理映射。

glEnable(GL_TEXTURE_2D);     // 使用二维纹理

 glBindTexture(GL_TEXTURE_2D, texture[0]);   // 选择使用的纹理

 return TRUE;      // 初始化成功

}

重置大小的代码没有变化,但DrawGLScene这部分代码有变化。 

  

这里是第一处变动。我们打算使用COS和SIN让物体绕着屏幕旋转而不是把它固定在屏幕中间。我们将把物体向屏幕里移动3个单位。在x轴,我们将移动范围限制在-1.1到+1.1之间。我们使用rot变量来控制左右移动

。我们把上下移动的范围限制在+0.8到-0.8之间。同样使用rot变量来控制上下移动(最好充分利用你的变量)。

// 设置字体的位置

 glTranslatef(1.1f*float(cos(rot/16.0f)),0.8f*float(sin(rot/20.0f)),-3.0f);

下面做常规的旋转。这会使符号在X,Y和Z轴旋转。

glRotatef(rot,1.0f,0.0f,0.0f);    // 沿X轴旋转

 glRotatef(rot*1.2f,0.0f,1.0f,0.0f);    // 沿Y轴旋转

 glRotatef(rot*1.4f,0.0f,0.0f,1.0f);    // 沿Z轴旋转

我们将物体相对观察点向左向下移动一点,以便于把符号定位于每个轴的中心。否则,当我们旋转它的时候,看起来就不像是在围绕它自己的中心在旋转。-0.35只是一个能让符号正确显示的数。我也试过一些其它数,

因为我不知道这种字体的宽度是多少,可以适情况作出调整。我不知道为什么这种字体没有一个中心。

glTranslatef(-0.35f,-0.35f,0.1f);    // 移动到可以显示的位置

最后,我们绘制海盗旗的符号,然后增加rot变量,从而使这个符号在屏幕中旋转和移动。如果你不知道我是如何从字母‘N’中得到海盗旗符号的,那就打开Microsoft Word或是写字板。在字体下拉菜单中选择

Wingdings字体。输入大写字母‘N’,就会显示出海盗旗符号了。

glPrint("N");      // 绘制海盗旗符号

 rot+=0.1f;      // 增加旋转变量

最后要做的事就是在KillGLWindow()的最后添加KillFont()函数,如下所示。添加这行代码很重要。它将在我们退出程序之前做清理工作。

原文及其个版本源代码下载:

http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=15

 

NeHe OpenGL教程 第十五课:纹理图形字的更多相关文章

  1. NeHe OpenGL教程 第二十五课:变形

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  2. NeHe OpenGL教程 第十四课:图形字体

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  3. NeHe OpenGL教程 第二十九课:Blt函数

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  4. NeHe OpenGL教程 第二十八课:贝塞尔曲面

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  5. NeHe OpenGL教程 第二十六课:反射

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  6. NeHe OpenGL教程 第二十四课:扩展

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  7. NeHe OpenGL教程 第二十二课:凹凸映射

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  8. NeHe OpenGL教程 第十九课:粒子系统

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  9. NeHe OpenGL教程 第十八课:二次几何体

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

随机推荐

  1. linux 命令-全称

    cal = CALendar calendar日历, 历法cat = CATenate 连接, 使连续cd = Change Directorychgrp = CHange GRouPchmod =  ...

  2. poj1270 拓扑序(DFS)

    题意:给出将会出现的多个字母,并紧接着给出一部分字母的大小关系,要求按照字典序从小到大输出所有符合上述关系的排列. 拓扑序,由于需要输出所有排列,所以需要使用 dfs ,只要点从小到大遍历就可以实现字 ...

  3. poj1094 拓扑序

    题意:现在有多个大写字母(不一定连续),给出字母之间的大小关系,问到第几个关系时就能判断有唯一大小排序或出现矛盾,或是有多个合理排序,若有唯一排序,则输出它. 拓扑序,只不过坑爹的是如果关系处理到一半 ...

  4. 【原创】Algorithms:原地归并排序

    第一次归并: a[0] a[1] a[2] a[3] a[4] a[5] a[6] 23 8 19 33 15 6 27 ↑             ↑ i     j 最开始i指向a[0],j指向a ...

  5. 关闭V-Ray warning消息框

    有时候模型使用低版本VR保存的,再次打开模型时会弹出V-Ray warning提示框 这个问题困扰了我一周时间.... 查了VR官方帮助文档 解决方法如下 setVRaySilentMode() -- ...

  6. Qt Creator调试

    与调试器交互的几种方法: 1.单行运行或者单指令运行 2.中断程序运行 3.设置断点 4.检查调用栈空间的内容 5.检查并修改局部或者全局变量 6.检查并修改被调试程序的寄存器和内存内容 7.检查装载 ...

  7. 复利计算- 结对2.0--复利计算WEB升级版

    客户在大家的引导下,有了更多的想法: 这个数据我经常会填.....帮我预先填上呗?...... 把界面做得简单漂亮好操作一点呗? 能不能帮我转成个APP,我装到手机上就更方便了? 我觉得这个很有用,很 ...

  8. Viewpager图片自动轮播,网络图片加载,图片自动刷新

    package com.teffy.viewpager; import java.util.ArrayList; import java.util.concurrent.Executors; impo ...

  9. QQ登入(1)-有客户端直接授权,没客户端web授权

    准备jar文件3个: android-support-v4.jar 下载地址:http://pan.baidu.com/s/1eQmoTm6 mta-sdk-1.6.2.jar 下载地址:http:/ ...

  10. sql compact 使用EF无法更新的问题?

    1.问题一是表中没有主键会报一些莫名其妙的错误. 2.数据库文件被默认复制到了Debug/Release目录,实际调试或运行时发现原有数据库没有被更新.