OpenGL 没有自带专门的字库,因此,要显示文字,就必须依赖操作系统所提供的功
能
1. OpenGL 版 “hello world”
#include <windows.h>
#define MAX_CHA 128
void drawString(const char* str)
{
static int isFirstCall = 1;
static GLuint lists;
if(isFirstCall)
{
isFirstCall = 0;
lists = glGenLists(MAX_CHAR);
wglUseFontBitmaps(wglGetCurrentDC(),0,MAX_CHAR,lists);
}
for(;*str != '\0';++str)
{
glCallList(lists + *str);
}
}
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1.0f,0.0f,0.0f);
glRasterPos2f(0.0f,0.0f);
drawString("Hello,World");
glutSwapBuffers();
}
指定字体
void selectFont(int size,int charset,const char* face)
{
HFONT hFont = CreateFontA(size, 0, 0, 0, FW_MEDIUM, 0, 0, 0,
charset, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, face);
HFONT hOldFont = (HFONT)SelectObject(wglGetCurrentDC(), hFont);
DeleteObject(hOldFont);
}
void dispaly(void)
{
selectFont(48,ANSI_CHARSET,"Comic Sans MS");
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1.0f,0.0f,0.0f);
glRasterPos2f(0.0f,0.0f);
drawString("Hello Word!");
drawSwapBuffers();
}
显示中文
通常我们在 C 语言里面使用的字符串,如果中英文混合的话,例如“this is 中文字
符.”,则英文字符只占用一个字节,而中文字符则占用两个字节。用
MultiByteToWideChar 函数,可以转化为所有的字符都占两个字节
len = 0;
for(i = 0;str[i] != '\0';++i)
{
if(IsDBCSLeadByte(str[i]))
{
++i;
}
++len;
}
wstring = (wchar_t*)malloc((len + 1) * sizeof(wchar_t));
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, str, -1, wstring, len);
wstring[len] = L'\0';
free(wstring);
加上前面的 wglUseFontBitmaps 函数,可显示中文字符
void drawCNString(const char* str)
{
int len,i;
wchar_t* wstring;
HDC hDC = wglGetCurrentDC();
GLuint list glGenLists(1);
len = 0;
for(i = 0;str[i] != '\0';++i)
{
if(IsDBCSLeadByte(str[i]))
{
++i;
}
++len;
}
wstring = (wchar_t*)malloc((len+1) * sizeof(wchar_t));
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, str, -1, wstring, len);
wstring[len] = L'\0';
for(i=0; i<len; ++i)
{
wglUseFontBitmapsW(hDC, wstring[i], 1, list);
glCallList(list);
}
free(wstring);
glDeleteLists(list, 1);
}
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT);
selectFont(48, ANSI_CHARSET, "Comic Sans MS");
glColor3f(1.0f, 0.0f, 0.0f);
glRasterPos2f(-0.7f, 0.4f);
drawString("Hello, World!");
selectFont(48, GB2312_CHARSET, "楷体_GB2312");
glColor3f(1.0f, 1.0f, 0.0f);
glRasterPos2f(-0.7f, -0.1f);
drawCNString("当代的中国汉字");
selectFont(48, DEFAULT_CHARSET, "华文仿宋");
glColor3f(0.0f, 1.0f, 0.0f);
glRasterPos2f(-0.7f, -0.6f);
drawCNString("傳統的中國漢字");
glutSwapBuffers();
}
纹理字体
把文字放到纹理中可以任意修改字符的大小。
如何把文字放到纹理中?
这里不是直接绘制到纹理,而是用简单的办法: 先把汉字绘制出来,成为像素,
然后用 glCopyTexImage2D 把像素复制贷纹理。
glCopyTexImage2D 与 glTexImage2D 的用法类似
前者是直接把绘制好的像素复制贷纹理中,后者是从内存传送数据到纹理中。
glRasterPos2f(XXX,XXX);
drawCNString("關");
glGenTextures(1,&texID);
glBindTexture(GL_TEXTURE_2D,texID);
glCopyTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,0,0,64,64,0);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINIEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LIINEAR);
glRasterPos2f(XXX,XXX) r如何计算这个显示坐标。
计算文字宽度:
Windows 专门提供了一个函数 GetCharABCWidths,它计算一系列连续字符的
ABC 宽度。所谓 ABC宽度,包括了 a, b, c 三个量,a 表示字符左边的空白宽度,
b 表示字符实际的宽度,c 表示字符右边的空白宽度,三个宽度值相加得到整个字
符所占宽度。
如果只需要得到总的宽度,可以使用 GetCharWidth32 函数。如果要支持汉字,应
该使用宽字符版本,即 GetCharABCWidthsW 和 GetCharWidth32W。在使用前需
要用 MultiByteToWideChar 函数,将通常的字符串转化为宽字符串,就像前面的
wglUseFontBitmapsW 那样。
计算高度:
指定字体的时候指定大小为 s 的话,所有的字符高度都为 s,只有宽度不同。
如果我们使用 glRasterPos2i(-1, -1)从最左下角开始显示字符的话,其实是不能得
到完整的字符的:。,因此我们应该设置字体的高度为 2 的整数次方,例如 16, 32,
64,这样用起来就会比较方便。
#define FONT_SIZE 64
#define TEXTURE_SIZE FONT_SIZE
GLuint drawChar_To_Texture(const char* s)
{
wchar_t w;
HDC hDC = wglGetCurrentDC();
selectFont(FONT_SIZE, DEFAULT_CHARSET, "");
glColor3f(1.0f, 1.0f, 1.0f);
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, s, 2, &w, 1);
{
int width, x, y;
GetCharWidth32W(hDC, w, w, &width);
x = (TEXTURE_SIZE - width) / 2;
y = FONT_SIZE / 8;
glWindowPos2iARB(x, y);
}
{
GLuint list = glGenLists(1);
glDisable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
glDisable(GL_FOG);
glDisable(GL_TEXTURE_2D);
wglUseFontBitmaps(hDC, w, 1, list);
glCallList(list);
glDeleteLists(list, 1);
}
{
GLuint texID;
glGenTextures(1, &texID);
glBindTexture(GL_TEXTURE_2D, texID);
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE4,0,0,TEXTURE_SIZE, TEXTURE_SIZE, 0);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_LINEAR);
return texID;
}
纹理字体应用
1. 用多个四边形(实际上是矩形)连接起来,制作飘动的效果
2. 使用光照,计算法线向量
3. 把纹理融合进去
#define MIN_X (-0.5f)
#define MAX_X (0.5f)
#define MIN_Y (-0.5f)
#define MAX_Y (0.5f)
#define SEGS ((int)((MAX_X - MIN_X) * (512/2)))
#define RANGE (0.05f)
#define CIRCLES (2.0f)
#define SPEED (5.0f)
#define PI (3.1415926f)
#include <math.h>
GLfloat theta = 0.0f;
void draw(void)
{
int i;
const GLfloat x_inc = (MAX_X - MIN_X) / SEGS;
const GLfloat t_inc = 1.0f / SEGS;
const GLfloat theta_inc = 2 * PI * CIRCLES / SEGS;
glBegin(GL_QUAD_STRIP);
for(i = 0;i <= SEGS;++i)
{
const GLfloat z = RANGE * sin(i*theta_inc + theta);
const GLfloat
v1[] = {i*x_inc + MIN_X, MAX_Y, z},
v2[] = {i*x_inc + MIN_X, MIN_Y, z},
v3[] = {
(i+1)*x_inc + MIN_X,
MAX_Y,
RANGE * sin((i+1)*theta_inc + theta)
};
setNormal(v1, v2, v3);
glTexCoord2f(i*t_inc, 1.0f);
glVertex3fv(v1);
glTexCoord2f(i*t_inc, 0.0f);
glVertex3fv(v2);
}
glEnd();
}
void idle(void)
{
theta += (SPEED * PI / 180.0f);
glutPostRedisplay();
}
void setNormal(const GLfloat v1[3],const GLfloat v2[3],const GLfloat v3[3])
{
const GLfloat s1[] = {
v2[0]-v1[0],
v2[1]-v1[1],
v2[2]-v1[2]
};
const GLfloat s2[] = {
v3[0]-v1[0],
v3[1]-v1[1],
v3[2]-v1[2]
};
GLfloat n[] = {
s1[1]*s2[2] - s1[2]*s2[1],
s1[2]*s2[0] - s1[0]*s2[2],
s1[0]*s2[1] - s1[1]*s2[0]
};
GLfloat abs = sqrt(n[0]*n[0] + n[1]*n[1] + n[2]*n[2]);
n[0] /= abs;
n[1] /= abs;
n[2] /= abs;
glNormal3fv(n);
}
缓冲机制
改进缓冲机制性能,应该使用更高效的置换算法。
#include "ctbuf.h"
void display(void)
{
static int isFirstCall = 1;
if( isFirstCall )
{
isFirstCall = 0;
ctbuf_init(32, 256, "黑体");
}
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_TEXTURE_2D);
glPushMatrix();
glTranslatef(-1.0f, 0.0f, 0.0f);
ctbuf_drawString("美好明天就要到来", 0.1f, 0.15f);
glTranslatef(0.0f, -0.15f, 0.0f);
ctbuf_drawString("Best is yet to come", 0.1f, 0.15f);
glPopMatrix();
glutSwapBuffers();
}
const char* g_string ="《合金装备》(Metal Gear Solid)结尾曲歌词\n因为。。。。。。。。 \n美好即将到来\n"
textarea_t* p_textarea = NULL;
void display(void)
{
static int isFirstCall = 1;
if( isFirstCall )
{
isFirstCall = 0;
ctbuf_init(24, 256, "隶书");
p_textarea = ta_create(-0.7f, -0.5f, 0.7f, 0.5f,20, 10, g_string);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
}
glClear(GL_COLOR_BUFFER_BIT);
glEnable(GL_TEXTURE_2D);
ta_display(p_textarea);
glEnable(GL_BLEND);
glDisable(GL_TEXTURE_2D);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glColor4f(1.0f, 1.0f, 1.0f, 0.5f);
glRectf(-0.7f, -0.5f, 0.7f, 0.5f);
glDisable(GL_BLEND);
glEnable(GL_TEXTURE_2D);
glPushMatrix();
glTranslatef(-1.0f, 0.9f, 0.0f);
ctbuf_drawString("歌词显示程序", 0.1f, 0.1f);
glTranslatef(0.0f, -0.1f, 0.0f);
ctbuf_drawString("按 W/S 键实现上、下翻页", 0.1f, 0.1f);
glTranslatef(0.0f, -0.1f, 0.0f);
ctbuf_drawString("按 ESC 退出", 0.1f, 0.1f);
glPopMatrix();
glutSwapBuffers();
}