设为首页收藏本站官方微博

汉化资料 【OpenGL汉化研究】OpenGL如何显示文字

[复制链接]
查看: 2334|回复: 0
打印 上一主题 下一主题

[汉化资料] 【OpenGL汉化研究】OpenGL如何显示文字

跳转到指定楼层
楼主
发表于 2010-1-23 16:50 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

【OpenGL汉化研究】OpenGL如何显示文字

原文 - p3 \5 ^0 W5 ]5 c0 g4 J6 H8 r
http://blog.chinaunix.net/u/26313/showart_1663543.html
7 Z) }: H$ f* R3 `+ j7 a/ j+ ?! O4 d" u( x  p) N+ q, q- {
内容超多的一课!不过我想精彩的程度也一定不会让大家失望。大家不妨先浏览一下课程里的图片:)。
& l1 g, F1 t4 X" \1 z* q# U0 ^
- y/ _0 ]4 W+ u0 p1 Y本课我们来谈谈如何显示文字。 8 G% T5 t: Y# w- f  k: M
OpenGL并没有直接提供显示文字的功能,并且,OpenGL也没有自带专门的字库。因此,要显示文字,就必须依赖操作系统所提供的功能了。
5 B# ~( M8 p8 z# B2 _# q' d各种流行的图形操作系统,例如Windows系统和Linux系统,都提供了一些功能,以便能够在OpenGL程序中方便的显示文字。
5 K( L) e7 w+ E4 {6 h( R3 @7 @最常见的方法就是,我们给出一个字符,给出一个显示列表编号,然后操作系统由把绘制这个字符的OpenGL命令装到指定的显示列表中。当需要绘制字符的时候,我们只需要调用这个显示列表即可。 0 n7 N6 O" F8 w' E/ m. a5 C
不过,Windows系统和Linux系统,产生这个显示列表的方法是不同的(虽然大同小异)。作为我个人,只在Windows系统中编程,没有使用Linux系统的相关经验,所以本课我们仅针对Windows系统。
0 S" d2 s3 H1 B# _
1 x3 I; j1 o! v+ LOpenGL版的“Hello, World!”
1 u% j( e/ P8 R7 l. p写完了本课,我的感受是:显示文字很简单,显示文字很复杂。看似简单的功能,背后却隐藏了深不可测的玄机。 - [- f& H6 n" Y* s/ h. x
呵呵,别一开始就被吓住了,让我们先从“Hello, World!”开始。
4 Q* X) ~) i& [$ P% c8 L# ^前面已经说过了,要显示字符,就需要通过操作系统,把绘制字符的动作装到显示列表中,然后我们调用显示列表即可绘制字符。 6 ]7 U$ n7 _+ h. a
假如我们要显示的文字全部是ASCII字符,则总共只有0到127这128种可能,因此可以预先把所有的字符分别装到对应的显示列表中,然后在需要时调用这些显示列表。 4 K; X9 n; i( e/ P
Windows系统中,可以使用wglUseFontBitmaps函数来批量的产生显示字符用的显示列表。函数有四个参数:   }: s, V6 {: N/ Q2 y. [
第一个参数是HDC,学过Windows GDI的朋友应该会熟悉这个。如果没有学过,那也没关系,只要知道调用wglGetCurrentDC函数,就可以得到一个HDC了。具体的情况可以看下面的代码。
* g2 H' l) j( t1 a! i3 v& V  R: }第二个参数表示第一个要产生的字符,因为我们要产生0到127的字符的显示列表,所以这里填0。 . `  `* j- [# q' r  n2 a" K
第三个参数表示要产生字符的总个数,因为我们要产生0到127的字符的显示列表,总共有128个字符,所以这里填128。 " l  u6 s5 D5 t/ t! k0 v3 _6 M2 z/ E
第四个参数表示第一个字符所对应显示列表的编号。假如这里填1000,则第一个字符的绘制命令将被装到第1000号显示列表,第二个字符的绘制命令将被装到第1001号显示列表,依次类推。我们可以先用glGenLists申请128个连续的显示列表编号,然后把第一个显示列表编号填在这里。
3 I8 }: c# |" D! E还要说明一下,因为wglUseFontBitmaps是Windows系统特有的函数,所以在使用前需要加入头文件:#include <windows.h>。
  n; P+ w8 w6 h7 {, P7 Q现在让我们来看具体的代码: ( U% b7 Y% `+ f% M, Q3 Y1 \

5 M" n- n6 a6 H# r#include <windows.h> ; j( ^$ O% P" K! h
5 r2 H$ a; |, [: v2 b0 F6 o; i
// ASCII字符总共只有0到127,一共128种字符 9 @6 \, ^/ x, x- y
#define MAX_CHAR      128 0 o* m" B! d2 k% Y8 O
: e) X( V6 T7 V! H
void drawString(const char* str) { 3 R4 U% r$ [2 B- A. A
    static int isFirstCall = 1;
/ w" J$ S- Z0 n0 p! C5 J$ K* Q    static GLuint lists;
- y/ S/ w3 q4 U! D$ {& M! M* W& H/ i* s; ]: K8 P# s+ G  P* K! C3 Y
    if( isFirstCall ) { // 如果是第一次调用,执行初始化 ; E: K4 g5 t3 p! K
                        // 为每一个ASCII字符产生一个显示列表
* L9 S/ q' }: y        isFirstCall = 0;
: e! u; ?) y  i/ m- T3 f, \7 `! P/ w
        // 申请MAX_CHAR个连续的显示列表编号 8 K' V  E9 p- u
        lists = glGenLists(MAX_CHAR);
, j* t" Y: ]7 I7 o# F0 c: B; E' o3 S; N/ F- Q' Y, A! `: q
        // 把每个字符的绘制命令都装到对应的显示列表中
8 G$ E4 K# q0 s, m1 A; a        wglUseFontBitmaps(wglGetCurrentDC(), 0, MAX_CHAR, lists);
) e/ c* p" T' z) E" q: F: U3 h    }
, |# @$ J- l7 V+ o; \    // 调用每个字符对应的显示列表,绘制每个字符
5 m2 V1 i, b& k, _    for(; *str!='\0'; ++str)
) S( l7 [0 n" w        glCallList(lists + *str); % d( R; c* Q( |' |2 O# f# D* y% T- k
} ! E. m; H/ ^4 i9 _8 w

. U3 m+ c, D7 y) l& \
7 R3 U" t- b1 O: B# A! ?  Y, M! S( \! Y0 _0 i1 o- `! z
显示列表一旦产生就一直存在(除非调用glDeleteLists销毁),所以我们只需要在第一次调用的时候初始化,以后就可以很方便的调用这些显示列表来绘制字符了。
' I5 \* z/ e# Z; N2 _# O) N绘制字符的时候,可以先用glColor*等指定颜色,然后用glRasterPos*指定位置,最后调用显示列表来绘制。 0 _1 W! H" k% E# c% A2 K6 i

1 |6 _* u$ e$ ]- cvoid display(void) {
' D/ i) R' j7 w0 ^    glClear(GL_COLOR_BUFFER_BIT); 9 [3 h' `, C* ^, D

  b+ {% W7 b; b# x. ]    glColor3f(1.0f, 0.0f, 0.0f); - f. S; o- w0 I( u% D6 V
    glRasterPos2f(0.0f, 0.0f); 0 m2 q0 w5 K( Y; S
    drawString("Hello, World!"); / N# ^( f3 ~; Y( y. |# A$ l  u, B

6 ~$ |2 n) O/ J' ]) O7 ?1 x- f    glutSwapBuffers(); ) d5 y0 g. l5 |( \" [8 b% B7 q- W
} , `. x( R2 B- i7 D) g: I

' x: q* c+ i$ l
! g4 s8 k9 O# i* y7 o: D
) B# w0 N( ?& y. |) `+ q# w+ a$ U效果如图:
8 R# z* G* b/ H. k& O# Y) m, V
2 I" i& }# F7 A* R& B) o7 B% G( t
指定字体 - \; b6 a+ B$ o# r
在产生显示列表前,Windows允许选择字体。
) ^1 }  O) A$ R+ e. l, C我做了一个selectFont函数来实现它,大家可以看看代码。
3 r9 [+ {  @  M7 A+ p/ T3 D6 F5 d! {/ E% G
void selectFont(int size, int charset, const char* face) {
# Y# l' f5 ]# a1 {8 h    HFONT hFont = CreateFontA(size, 0, 0, 0, FW_MEDIUM, 0, 0, 0,
/ h( p0 v, H2 G" e/ U        charset, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
6 e5 z1 {( s6 ?* b# m0 L        DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, face);
8 P* c0 K) z9 F6 }) ?: u& L    HFONT hOldFont = (HFONT)SelectObject(wglGetCurrentDC(), hFont);
) k0 z) e. N% m3 S    DeleteObject(hOldFont); 8 u8 E% l  p: u9 q$ x
} 4 E6 i3 x" p5 K- D9 Q

9 |: I. R$ q4 w  g. Avoid display(void) { ; y* f9 O( e0 }% j- b& K' ~
    selectFont(48, ANSI_CHARSET, "Comic Sans MS"); ' t8 f/ M/ ^8 ?& I4 G
& Q- _" e( ^+ z4 i1 m$ y1 ]
    glClear(GL_COLOR_BUFFER_BIT); 9 |+ m2 ^3 S* Z, G1 O
4 k' u; R2 {6 R$ ^
    glColor3f(1.0f, 0.0f, 0.0f); " D( D% ?6 F' P9 I" L
    glRasterPos2f(0.0f, 0.0f); 0 c/ I: a, E+ X1 V
    drawString("Hello, World!"); ' Z, }) V* P, R/ z) A

) C; w3 z, k" [; v    glutSwapBuffers(); 5 I/ t( J/ T( c# m, I+ l% V5 L- ?& f
}
) h' s3 K6 N; z- [5 @$ u0 D! X5 f- h6 I$ ^
' Z) [) [* R% Y
最主要的部分就在于那个参数超多的CreateFont函数,学过Windows GDI的朋友应该不会陌生。没有学过GDI的朋友,有兴趣的话可以自己翻翻MSDN文档。这里我并不准备仔细讲这些参数了,下面的内容还多着呢:( 5 s8 ~  L* H4 l# Q- H
如果需要在自己的程序中选择字体的话,把selectFont函数抄下来,在调用glutCreateWindow之后、在调用wglUseFontBitmaps之前使用selectFont函数即可指定字体。函数的三个参数分别表示了字体大小、字符集(英文字体可以用ANSI_CHARSET,简体中文字体可以用GB2312_CHARSET,繁体中文字体可以用CHINESEBIG5_CHARSET,对于中文的Windows系统,也可以直接用DEFAULT_CHARSET表示默认字符集)、字体名称。
) ]( Q6 L  [+ M# `; F' y# d效果如图: 2 H2 {4 l- d% T2 n4 n: t

/ f* w% O' d# U  x8 j# s5 _& ]8 d8 O5 h; i1 t
显示中文
) @9 ^( Y; h9 l6 ^原则上,显示中文和显示英文并无不同,同样是把要显示的字符做成显示列表,然后进行调用。 4 |6 i/ M7 I3 P
但是有一个问题,英文字母很少,最多只有几百个,为每个字母创建一个显示列表,没有问题。但是汉字有非常多个,如果每个汉字都产生一个显示列表,这是不切实际的。 - \% W" ^3 x9 _) ]8 J0 ?9 y" \
我们不能在初始化时就为每个字符建立一个显示列表,那就只有在每次绘制字符时创建它了。当我们需要绘制一个字符时,创建对应的显示列表,等绘制完毕后,再将它销毁。 ; N/ b6 U( N5 c
这里还经常涉及到中文乱码的问题,我对这个问题也不甚了解,但是网上流传的版本中,使用了MultiByteToWideChar这个函数的,基本上都没有出现乱码,所以我也准备用这个函数:) . s$ ?2 z4 O( F& j4 `% Q
通常我们在C语言里面使用的字符串,如果中英文混合的话,例如“this is 中文字符.”,则英文字符只占用一个字节,而中文字符则占用两个字节。用MultiByteToWideChar函数,可以转化为所有的字符都占两个字节(同时解决了前面所说的乱码问题:))。 9 P; x( |. R& @3 T% y  `
转化的代码如下: / D5 K/ u- U, p8 z" }! P" K

# |' B  s) ^- W2 Y4 G. i# R// 计算字符的个数   T5 V7 z/ R! y# b6 F6 A, ^
// 如果是双字节字符的(比如中文字符),两个字节才算一个字符
; [& o8 g2 `( \- p9 f, `( t// 否则一个字节算一个字符 ' Z& A' A! J8 _# V8 ^
len = 0;
( D) a1 U- I4 m2 ~7 i8 mfor(i=0; str!='\0'; ++i)
# H: ]" h) r# Z{
& [# X$ l$ o% X6 n6 `    if( IsDBCSLeadByte(str) ) ! L7 q8 I" e" D! l$ ^5 {- \
        ++i;
) H+ I; p  P4 S  p! n5 w  L    ++len;
$ w, v7 l6 ]/ R6 G} 4 l* d- {* Y. q8 D- d: U! Z

3 d7 H% t$ L/ _0 N// 将混合字符转化为宽字符 3 w! X# p7 h: i) L0 |
wstring = (wchar_t*)malloc((len+1) * sizeof(wchar_t));
7 _  v* s1 _% }, j  MMultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, str, -1, wstring, len); 3 g; G3 C5 d2 B* r) P
wstring[len] = L'\0';
/ Q" z. k2 `/ F/ G0 X+ Q: t& {% w7 S9 v0 e
// 用完后记得释放内存 / }* H: s* Y# b( p- Z- X9 e
free(wstring); / |, w% Z4 U( N) ?6 s9 _! l4 {
- J* n6 t. T& f
9 D! c# }3 j7 U

5 H6 V  y" O& f4 ?$ M加上前面所讲到的wglUseFontBitmaps函数,即可显示中文字符了。 # ]1 a" U+ F# w/ H4 F7 z. |4 Q

1 j, i# `- M$ r5 Kvoid drawCNString(const char* str) { # i' E& T/ o! P0 O# x" {, h
    int len, i;
0 b+ e$ T, @8 ?, W    wchar_t* wstring; ' i' z0 h" H* Z  }5 G0 g5 L  z1 ?
    HDC hDC = wglGetCurrentDC(); $ Z$ z1 @3 q. u
    GLuint list = glGenLists(1); * K; d# n( k$ M
) a3 E  G! [# b
    // 计算字符的个数
! }) t2 g8 |* ]6 C: S7 z    // 如果是双字节字符的(比如中文字符),两个字节才算一个字符 ! b, r2 _6 [0 f6 F) L
    // 否则一个字节算一个字符 2 S7 b2 M9 z/ Q& w2 D# j' f
    len = 0; , C0 ?  {& B* ?) l/ c# C
    for(i=0; str!='\0'; ++i)
: J5 _6 F: I0 W9 ?4 u3 q, W    {
- S0 R5 G9 Y& |" {% i3 g# }        if( IsDBCSLeadByte(str) )
$ k- W1 E/ U. \- w% b# Z) W4 ~) H( ]            ++i;
! z' P6 ~3 u1 a/ v0 c3 P        ++len; 6 J( l1 ^' b* \/ w1 A! N3 E
    }
2 X+ ?- r( p& o5 s: b' F7 m1 B6 Q0 _. g; }  j" u' K* p: k
    // 将混合字符转化为宽字符
7 k! b) e4 M0 p1 H" M. ^! p    wstring = (wchar_t*)malloc((len+1) * sizeof(wchar_t));   o7 K) `5 }7 k
    MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, str, -1, wstring, len);
5 d8 b' C1 x  F# ?    wstring[len] = L'\0';
5 B, l: \. z4 t% ]
) g% C) M+ D# n) _* h    // 逐个输出字符
  ~! v8 Y6 i3 N. n3 A, s2 P4 c    for(i=0; i<len; ++i)
1 X2 n% {5 W" b    { / T9 k( K- y# k# W: ]
        wglUseFontBitmapsW(hDC, wstring, 1, list); 0 {0 f* [: ]7 K0 D& q; j
        glCallList(list);
0 z7 w, g, m' k& S' V0 S/ j    }
4 c! x3 w" Y4 k1 v: c# @: I/ T: b: ?4 X) I: S/ F
    // 回收所有临时资源
, v% \8 W* }) W& f- M. e    free(wstring); % T0 k1 Y1 y6 S4 D# `
    glDeleteLists(list, 1); ) u8 C- A6 h* \# m6 k) r
}
5 y: G# @. Q8 \/ _. W: A& c) {+ A; u& R$ `" g$ h7 c* D. i3 E
4 }9 f4 l7 J; P' e3 w
3 c  j, v- e( B- W/ o2 I
注意我用了wglUseFontBitmapsW函数,而不是wglUseFontBitmaps。wglUseFontBitmapsW是wglUseFontBitmaps函数的宽字符版本,它认为字符都占两个字节。因为这里使用了MultiByteToWideChar,每个字符其实是占两个字节的,所以应该用wglUseFontBitmapsW。
6 [2 l% i3 I4 f  T) m+ r! C# {* }4 P6 Z. h
void display(void) { - x; h' B# R, K0 _& T  v
    glClear(GL_COLOR_BUFFER_BIT); 9 D( b$ |$ g4 A% q

! a( j6 Y- m/ |) ?    selectFont(48, ANSI_CHARSET, "Comic Sans MS");
1 t0 I$ B! q# g* u7 L# m$ e    glColor3f(1.0f, 0.0f, 0.0f);
; z; J' _! e8 \; `0 w    glRasterPos2f(-0.7f, 0.4f);
* S, @& h; g2 U+ T4 U    drawString("Hello, World!"); : ^; ?$ v9 ?; D1 r/ J0 j. `2 m$ W+ U7 H

5 b: c+ Z6 K" T' I9 x/ q7 c    selectFont(48, GB2312_CHARSET, "楷体_GB2312");
% F7 S6 N+ O  r7 N1 J0 ~& I* t) \* m    glColor3f(1.0f, 1.0f, 0.0f);
5 G7 ^7 E% o' w% e    glRasterPos2f(-0.7f, -0.1f); 8 U; N! r3 b0 B* N+ D' c
    drawCNString("当代的中国汉字"); 3 H; o! |7 Z$ P0 \

* n+ C+ ?2 Z; g$ x! [0 h( B0 n    selectFont(48, DEFAULT_CHARSET, "华文仿宋"); , C2 q. n* Q% q6 D# e% @' ^
    glColor3f(0.0f, 1.0f, 0.0f);
: y# V! {; H& w- F. I8 A    glRasterPos2f(-0.7f, -0.6f); , r, o) m7 H0 \8 H$ r: C
    drawCNString("傳統的中國漢字");
7 b, P2 B; ~! o( ?% u1 Z
& H& m2 ?; I- Y2 |) q3 x    glutSwapBuffers(); # O' n3 A% L3 D
}
' V4 C& {$ J8 J2 e, I5 j$ R2 H9 `% }8 ~

7 t( o; K6 e- s* s# l3 `效果如图:
0 C* Y) P. L1 D9 Q1 I7 u

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏1 分享分享 很美好很美好 很差劲很差劲
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

冒险解谜游戏中文网 ChinaAVG

官方微博官方微信号小黑屋 微信玩家群  

(C) ChinaAVG 2004 - 2019 All Right Reserved. Powered by Discuz! X3.2
辽ICP备11008827号 | 桂公网安备 45010702000051号

冒险,与你同在。 冒险解谜游戏中文网ChinaAVG诞生于2004年9月9日,是全球华人共同的冒险解谜类游戏家园。我们致力于提供各类冒险游戏资讯供大家学习交流。本站所有资源均不用于商业用途。

快速回复 返回顶部 返回列表