Nibiru经测试,用了OpenGL的wglUseFontBitmaps,glCallLists函数来显示文字。 * d) G2 \! }8 {& ~
也许是一个汉化的突破口? 9 }/ g T9 ^( q6 t5 w
j! G' X, J# G( [9 J
---- 本文详细讨论了在OpenGL中显示文本的几种方法。
, V9 g7 [. M6 N$ K. B5 _/ e3 B% i1 D1 ?3 @/ q- y
----也许大多数程序员使用OpenGL更多的是将精力集中于动态三维图形应用,因此,OpenGL中的文本显示往往被忽视,使人有不见积薪之感。本文介绍了几种文本显示的方法,希望能对使用OpenGL的编程者有所帮助。
, h/ c M4 [5 S: X. b" n J& A
/ ~' _* F( c; [% B建立并修改程序
P0 z1 D' @, r. E: A4 j----建立一个MFC SDI Windows应用工程Text,除单文档属性外,使用其他的所有默认选择。在菜单Project打开Settings对话框,在Link属性页的 object/library modules编辑框中加入opengl32.lib glu32.lib glaux.lib三个GL库。我们利用这些库函数完成图形编辑工作。 ' \( M" Q9 T. u* J4 [ }5 O/ q( n( {
----为使VC++的AppWizard产生的SDI应用程序能使用 OpenGL绘图,还需要作一些修改,说明如下。
1 e& T" D0 J5 ~6 R
- ]2 R' C& N; i, F( r* w1 Q/ I----1.介绍PreCreateWindow函数 ' d2 e8 Q- z* O% ~" p! t, v
( R0 ]+ ]- r9 P. p: z5 @% Z---- OpenGL窗口必须具有WS_CLIPCHILDREN(创建父窗口使用的Windows风格,用于重绘时剪裁子窗口所覆盖的区域)和WS_CLIPIBLINGS(创建子窗口使用的Windows风格,用于重绘时剪裁其他子窗口所覆盖的区域)两种风格。此外,窗口类属性不能包括CS_PARENTDC风格。具体程序实现如下: . K* C! c& o. T$ d
W! l+ |" V+ \( g* N6 L% j$ T3 QBOOL CTextView::PreCreateWindow
) ]9 y; @* _, g: M(CREATESTRUCT& cs)
2 `* m! t) I7 T/ ], Z- n9 V5 X5 ^* l{
+ c/ {7 {5 ]+ y, G% X, c// TODO: Modify the Window class or styles here by modifying . u. k4 S3 {$ z+ z% X# \% j
// the CREATESTRUCT cs
* p3 p. L$ h6 J
( s, z( ~7 |# F0 e& `//An OpenGL window must be created with the following flag % r- X5 t. P0 Y
// and must not include CS_PARENTIDC for the class style.
% T8 I8 w$ R3 {8 ?cs.style|=WS_CLIPSIBLINGS|WS_CLIPCHILDREN;
% ~) n. \) i6 z+ a- y9 y6 ^+ o( V
F6 ~9 L# S- _3 y6 r6 h7 V7 @return CView::PreCreateWindow(cs); ' `: l8 q9 p: G) B) A/ |7 C
} : o' H' b1 I/ ?0 w( E( A
3 M6 b4 l; h5 J6 n5 K
----2.OnCreate函数中定义像素格式PIXELFORMAT和创建 RC
& L4 ]8 X( O& ]2 |6 w, g) U- a7 Y% I; `0 y
----要使窗口支持OpenGL绘图,必须对窗口进行初始化。其中包括定义像素格式PIXELFORMAT和创建RC,为OpenGL指定一个合适的像素格式,创建着色上下文并将它和窗口的设备上下文关联起来。着色上下文保存着当前着色环境的信息。可在OnCreate中调用一个自建视口成员函数SetupPixelFormat(),具体函数如下:
0 S5 P% I& |# O) c$ [# v" t4 U# |
9 y- u8 ~4 z+ C. L4 nBOOL CTextView::SetupPixelFormat() 1 D8 E9 L+ e0 i( b3 N; F5 T9 F7 i: s
{
# d" N3 O9 q, O5 T//Create a rendering context 0 d& o q$ ~$ q
CDC* m_pDC=GetDC(); . o- m' S( H4 ^& ]% |3 K
if(m_pDC==NULL) //failure to get DC
2 { n7 K. p( e: ?{
1 E8 h e+ T. b) N1 eMessageBox(“Could't get a valid DC."); 6 `4 c2 d( [1 M9 \, x
return FALSE;
* R- L! {+ s/ _' {$ { G$ z4 n} % m m3 u4 X, K% k6 E: w7 [7 v
9 W+ O& \! F$ {/ R1 I+ v//Default pixel format is a single-buffered, * t( `, T# I6 z9 j$ X; P* _( O# D
//OpenGL support hardware-accelerated,RGBA mode format 4 }/ |. M D2 o1 k% J& D1 c
PIXELFORMATDESCRIPTOR pfd = 7 @/ S) x5 P: X
{
2 s( `) ]6 q3 g* P+ _' C/ l8 @5 v2 x1 psizeof(PIXELFORMATDESCRIPTOR),//Structure size. ; C9 J/ a6 v2 }* W6 a: H+ o
1, ( I3 z" Z) O4 X! r5 m; ]6 ~
// Structure version number.Property flags(特性标志):
. N# V b2 y9 P! A1 @1 x2 r5 N: _PFD_DRAW_TO_WINDOW | // support window 7 I$ I! ]" k/ C
PFD_SUPPORT_OPENGL | // support OpenGL 8 o% {9 o0 s" D* q% D
PFD_DOUBLEBUFFER,
; Z' b; e' l8 i9 d) P, F1 P" kPFD_TYPE_RGBA, // RGBA type % R4 n" a2 B4 H0 a0 w8 i: z2 H
24, // 32-bit color. 3 w: t5 l8 G7 u n
0, 0, 0, 0, 0, 0, // Not concerned with these:不涉及的属性 ; S( I' @- g. R% f; ~
0, // No alpha :无alpha缓存
9 t. r! o5 D3 z0, // Shift bit ignored:忽略转换位 5 ~ k4 ?) j* _8 n, x. N
0, 0, 0, 0, 0,// No accum buffer:没有累积缓存
7 c+ {% x: J* K: [) b0 M/ S" F8 m* p32, // 32-bit depth buffer.
/ M* f, D4 i- l1 j! N5 y- ^, t1 N0, // No stencil:无模板缓存
+ a$ x" I. `2 s7 k: R+ k" S3 V& v0, // No auxliliary buffers:无辅助缓存 8 Q- m) H: z% S7 u3 t' }: B
PFD_MAIN_PLANE, // Main layer type.:主层类型
# ^. Y- q0 H# \8 X$ j0, // Reserved.:保留结构数
; y8 k5 f- q# U- R% C0, 0, 0 // Unsupported.:不支持结构数
) s3 |( [4 U- w# h/ V}; : L4 h' w$ [: T& T$ h/ _
int nPixelFormat= 0 M2 c- g% ?; `" R9 O+ Z
ChoosePixelFormat(m_pDC->GetSafeHdc(),&pfd);
+ E* ~* r( K2 `! hif( nPixelFormat ==0) 9 _; G5 e9 Y2 W+ c' y2 _
{ ! `- }$ [+ J+ e$ B# f2 ^
MessageBox(“ChoosePixelFormat failed."); 0 j8 d- b, J& o) w- |
return FALSE; # o' U! O' g5 M8 I5 c# Z- @5 x
} " h- |4 ~+ I# U# O ], }
+ n# K9 E( n) a3 q. mif(SetPixelFormat(m_pDC->GetSafeHdc(), . y7 s' l$ w$ K% h
nPixelFormat,&pfd)==0)
8 |# A8 @! }" I- V6 v% f{ 7 P5 P0 H4 ?) M9 I6 _( A
MessageBox(“SetPixelFormat failed."); ' e; D4 V- i# M$ s
return FALSE; 9 {6 i! k" }) t( b/ p
} " s# ~& }9 Y! m Y8 h
) M& H( g: x' ]" Fif( (m_hRC=wglCreateContext(m_pDC-> * z6 l; X! A& ]6 @
GetSafeHdc())) ==0)
" P9 C, b3 c9 H: w) q' v1 W7 a{
' j6 c( \( i* v& y' v9 H! O, {MessageBox(“wglCreateContext failed.");
: Y% |: ?% Z0 b. c, ^" lreturn FALSE; ' y# j$ B8 o0 f0 P6 Y
}
; C Q8 p' {6 i$ M8 Iif( (wglMakeCurrent(m_pDC->GetSafeHdc(),m_hRC)) ==0)
9 @- e# y4 c: _1 R. I{ 9 |6 [+ V) i. p0 O1 `* c
MessageBox(“wglMakeCurrent failed."); ' ~! H/ D# O. u
return FALSE; 5 @% _, [2 D9 `% d g
} ; p4 ^* a+ G- P: H# V
& n/ a& N$ w9 H' E8 d' }if(m_pDC) ReleaseDC(m_pDC); : o% y) A0 n% O% [$ j4 e
return TRUE; E: U8 S* M9 V2 `; J, z
} 5 {- X2 I$ A, j5 C1 f7 C
5 Z$ o" U% W$ x2 a----3.在OnCreate()函数中调用初始化背景函数 InitializeOpenGL() + G5 a) o$ a: O/ Z( O0 X4 i, _; B
r* H1 j1 M# ?
void CTextView::InitializeOpenGL()
, r1 w7 |, l$ @' u{ 3 H: m$ K/ |" c7 J* W' P! h
glClearColor(0.2f,0.2f,0.2f,0.0f); 7 v: m3 n4 u+ k0 j& F( v
glClearDepth(1.0);
, f$ Q" q3 x6 P8 s& JglDepthFunc(GL_LESS); , v5 k0 Y9 C6 w% ]
glEnable(GL_DEPTH_TEST);
/ V: |& g) ^1 }' g0 XglShadeModel(GL_SMOOTH); - R. ]3 W" X, s. c5 h9 h
} 1 D0 f, \0 r$ W
2 u; m/ Q9 G0 z----4.在OnCreate()中启动动画定时器
! ^7 s2 S$ } k, S$ r3 }0 {7 K9 I" E4 F$ M
SetTimer(0,40,NULL); 4 f! t7 E/ y9 S, u7 m6 |5 N3 ?
$ f! H& ~! _3 P. ~# Q* E----5.在收到WM_SIZE消息时要重新计算场景尺寸,用OnSize 设置图形显示模式
~- `" J0 i2 A3 b( `' y, D7 ^# X$ i W+ x8 M. M( t5 r
----为了使物体能合适的显示,必须要经过投影和确定视口的工作。 ( O9 Q/ v, z" i4 r* P' `4 m" u
8 N4 w2 e* l9 m. X
void CTextView::OnSize(UINT nType, int cx, int cy) 5 i7 {. m0 C8 i" `8 N
{
5 @8 P; E; F2 _6 G' E& `CView::OnSize(nType, cx, cy);
5 t/ I7 X9 F5 U5 U* `3 p7 @2 \
// TODO: Add your message handler code here 5 f5 t2 k: Q+ B( b
//Save the wide and height of the current window Client o' m# a p; _
GLsizei nWidth=(GLsizei)cx; * l0 e1 p: L2 V7 P- i8 H
GLsizei nHeight=(GLsizei)cy;
K* U& t9 F4 s: ]0 g4 tratio=(double)cx/(double)cy; $ o7 r+ S: o$ `+ z! R$ k3 N
& v: @4 @- w" J. L0 R" u z Z//Coupute the aspect ratio
, C; x3 | Y0 b V/ {0 b& D. v( vGLdouble dAspect=(GLdouble)nWidth/(GLdouble)nHeight; ' \1 }% y& n4 g1 @
" U2 Q9 p; h" C% C. EglViewport(0,0,nWidth,nHeight); $ K5 u" J. t' k+ u8 n4 n) c
glMatrixMode(GL_PROJECTION); 8 X& }5 N& c. c7 ^/ _' k6 j9 O
glLoadIdentity(); : n5 E: ^6 x8 H7 g# E/ o( R7 m
gluPerspective * v" W% B( t2 t) ?
(FOV,dAspect,NEARPLANE,FARPLANE); 3 a) I7 j& e0 R& D
% c1 q3 U `6 d+ s' u( i& ZglMatrixMode(GL_MODELVIEW);
' n. u! Y1 _. v- h( A t7 @glLoadIdentity(); ) j, Y4 l" N2 E# g2 x
} * E' p& `4 G& |: Z1 S
, f+ i7 S/ p$ u5 G3 s1 |; \----6.在OnDraw()中简单调用DrawScene()以执行OpenGL函数 0 i/ h1 X8 X: g/ z$ X+ z& Z
/ u' G r" j) I, u: T
void CTextView::OnDraw(CDC* pDC) 4 H' _# U" s. Q( i' w" a& u3 z
{
# E) Y# A$ ^1 w2 k2 Q4 S/ a' s1 _; T: HCTextDoc* pDoc = GetDocument(); . G+ e; D* H0 E/ M
ASSERT_VALID(pDoc);
0 u7 j+ O& }4 W. f( g- Z7 E, \% ^, o! R$ K3 F3 L% ^4 w2 i- @9 K1 h' Q* Z
// TODO: add draw code for native data here * E& j5 g0 l0 Z4 v3 x- U
DrawScene(); 6 q/ \3 h. L& v6 t) K9 G8 d0 C9 @2 J
//Invalidate();
$ U, C& ~5 H3 e% S+ ^5 w* {) n}
# o: P. g/ m) B/ w' B
0 v1 T4 A+ t! n( {" F* V2 k----7.撤销视窗时删除上下文并撤销定时器
) Z6 H; n7 m2 E8 |3 Y* F* M w' P7 @1 F
- m/ S9 w7 D! evoid CTextView::OnDestroy()
) s7 {! A( v3 m. K5 t{ - M6 I1 D" y6 k, v% g# V l& l
CView::OnDestroy();
9 ?/ I. x7 ~( ~
! A2 @0 X6 F" P; p+ E3 d// TODO: Add your message handler code here
u8 h1 @% H- p9 ~) W//This call makes the current RC not current
7 V+ h0 ~: u- U1 D8 sif(wglMakeCurrent(0,0)==FALSE)
+ v' Y7 m" y- ]1 vMessageBox(“wglMakeCurrent failed.");
2 G9 K2 J5 J: o9 v% G
' J& T I( |( f) t, E1 x/ B//delete the RC % ^; y u5 ?5 M7 g/ ~5 p
if(m_hRC && (wglDeleteContext(m_hRC)==FALSE)) 1 q/ S6 }/ u( R K: Q+ z
MessageBox(“wglDeleteContext fail.");
" m1 J& P. s# ~- XKillTimer(1); ! D% y2 c w1 h( G+ B
} " W q, V& b, a8 P9 \* u/ `! G
, @( z0 x, `7 w% F) J' G ] w; a
----8.当40ms定时器时间到时,简单地将整个场景的客户区置无效,使之重画 6 a! `! Z* O+ Y# y5 \1 o
2 t- G& N7 y" P3 f, n
void CTextView::OnTimer(UINT nIDEvent)
# _, _3 A$ G8 w, i0 Z \- k$ k{ ' Z' v4 O0 I" U3 W0 n% ^' t/ |7 o
// TODO: Add your message handler code here and/or call default
5 d; A% |0 C6 ?0 [Invalidate(); # m- }* I' Y- q2 k2 ^2 |
CView::OnTimer(nIDEvent);
3 Q1 g1 x2 Y" [, _% B} ; T% ?6 Z c2 m5 K3 H L# D% p* V
8 M+ f3 h9 u/ f3 `; X& X1 [
修改界面 2 |+ o/ K5 Y5 E; T
---- 删除菜单IDR_MAINFRAME中File下除去Exit菜单项外的所有选项,添加“显示GDI文字” “列表制作文字”“列表三维文字”三个菜单项,并给他们分配适当的ID。使用ClassWizard为这三个菜单项在视类中添加命令处理函数和界面更新函数,其中显示GDI文字的对应的函数如下: ! p5 J/ w1 l) u/ y4 ~
void CTextView::OnGdiText()
5 _ o! N( F, @: g0 i5 N{ % S( J8 p% G" e/ J8 e
// TODO: Add your command handler code here
1 t3 R8 V0 \* M+ g" c7 ]3 ]: am_iWhichText=0; 2 L# D7 X1 g& B0 _. T0 B' Y
Invalidate(); * R* S( J- V/ {' x0 C6 w) v1 M
}
2 V" ~" w; n8 \- B3 A$ m! N8 Z7 h% G8 H& V- r: U3 @
void CTextView::OnUpdateGdiText(CCmdUI* pCmdUI)
4 ?4 k c- n7 T0 Y& w9 B+ w. ?{ 6 E8 I$ E' [2 |
// TODO: Add your command update UI handler code here ( h# L! C6 G$ ]; m
if(m_iWhichText==0) pCmdUI->SetCheck(); i [( Y. c5 i( |# W4 y- n2 k" _# H: x2 j
else pCmdUI->SetCheck(0);
" i- m: h* X- n9 z}
( H0 F7 ?: ?" z
- k3 R$ E3 x& ^; G: m----增加Draw3DText()、DrawListText()和DrawGdiText()三个函数用于三种不同的文本绘制方法。增加DrawScene()函数,它被OnDraw函数调用,用于绘制场景。在DrawScene()函数中,当m_iWhichText为0、1、2时,分别调用 DrawGdiText()、DrawListText()和Draw3DText()显示文本。具体函数实现如下:
9 O7 |" M9 C# N' i
7 x0 o$ S7 s H% i& b5 _' A. A" vvoid CTextView::OnDraw(CDC* pDC) ( [ s3 S# R. J1 g2 C
{ 7 ^% t E5 O% U9 y) [, i
CTextDoc* pDoc = GetDocument(); ' Q1 V K9 V9 E7 K
ASSERT_VALID(pDoc);
7 u; z( H& [: H+ n5 U3 M
, v5 W, V' z$ d. Q// TODO: add draw code for native data here
: g. H! G9 z' uDrawScene(); 7 L% K$ f5 Z& ]+ K% K2 c
//Invalidate();
7 E4 Y) o" R2 s2 {} 0 }4 r) x) V+ k; Q; d
1 ]# M X7 {7 _; M/ i0 N: I
void CTextView::DrawScene() / n, b" T# l1 [( O
{ / B. G% e" }6 F' ]
glClear 0 P* q0 u; e2 c: y* }
(GL_COLOR_BUFFERBIT|GL_DEPTH_BUFFER_BIT);
" D6 P- `& o5 c& r9 h; {, s" V( l" I7 G2 ^9 V1 ^
glPushMatrix(); , h T- ?/ ]& T
glTranslatef(0.0f,0.0f,-FARPLANE);
G4 I F9 e* V* k0 ^2 T, j& |//TextureMap(); 0 n& S! j$ y" T) O7 Q
glPopMatrix();
) q9 B3 ^+ X/ P$ w6 P( GglPushMatrix(); p. y& W, l0 c. v$ E7 {
glTranslatef
3 \( D4 n+ {) C/ {1 ]& a(0.0f,0.0f,-(FARPLANE+NEARPLANE)/2); ) a( }5 ]5 V5 c) V0 G3 g
# q0 ~: x3 f# J! I( s% E, X
if(m_iWhichText==1) DrawListText(); - f7 i* x2 f0 @+ A) i2 `8 a
if(m_iWhichText==2) Draw3DText(); / j) Q# v7 ]) _5 m1 x
glPopMatrix(); 2 S8 T- R+ G( W5 o% ^5 j' U- @
glFinish(); 5 H( |/ i8 H$ s- P/ D- n% b
SwapBuffers(wglGetCurrentDC()); 1 [; p0 L- p) h0 U% b( O4 s
& l( d& t. c _
if(m_iWhichText==0) DrawGdiText(); & X+ [5 O4 ]1 S* Y& Y6 [7 d, G; s F0 U
}
4 z; x) q* G& O$ Q
' ~- q0 z! D* e IGDI 显示文本 $ r0 v' h+ }+ I* M. s
---- 调用wglGetCurrentDC()函数取得当前的设备上下文,使用TextOut函数显示文本,不过要注意在DoubleBuffer模式下,绘制函数要在glFinish()和 SwapBuffers(wglGetCurrentDC())函数之后调用,否则会产生闪烁,在绘制OpenGL结束之前使用GDI函数,要除去闪烁则只能使用SingleBuffer模式,具体函数如下: $ C6 R/ Z9 N, m8 q2 R+ r
void CTextView::DrawGdiText() " m6 Z+ e7 l& f5 A2 [* u
{ D& {8 A1 ?" C, y
HDC hdc=wglGetCurrentDC();
; r- E$ q) A+ K5 V% ?::SetBkMode( hdc, TRANSPARENT );
" U, g' g- b0 W0 w% E::SetTextColor( hdc, RGB(250,0,0) ); / n7 Y; ]& k. K& z" h3 Y
0 X8 `8 N0 V O5 R; mCString sState(“显示GDI文本。");
( v, H& G) q; b( N::TextOut(hdc,5,5,sState,sState.GetLength()); 7 o( N! V7 ?9 r B" F
}
! a9 N8 h" z9 j4 B. U" v& {, J
2 i v5 I- ^, R$ fwglUseFontBitmaps + f- N! D; Q0 J# F8 \; G5 k% m
函数显示文字
% V2 b( [, I# d( N----使用wglUseFontBitmaps()将ASCII字符装入显示列表,然后使用glCallLists()函数利用显示列表序列显示文本。wglUseFontBitmaps有四个参数,分别是当前使用的DC、从第几个ASCII字符起始装入列表、装入列表的ASCII字符数和起始的列表序号。glListBase()指定glCallLists执行的起始列表序列号。glCallLists()含有三个参数:执行列表序列的个数、列表值的类型和所要显示的文本。注意如果所要显示的文本是字符串,它所提供的信息是相对于起始装入ASCII字符的偏移量,因此最终所显示的ASCII字符是从glListBase()所指定的列表起始号在经过glCallLists()中偏移后的列表,因此wglUseFontBitmaps的从第几个ASCII字符起始装入列表参数、glListBase()指定的 glCallLists执行的起始列表序列号和glCallLists()中的所要显示的文本参数都可以影响最终显示结果。由于显示的是ASCII 字符,因此不能显示汉字。glRasterPos3f函数决定在 OpenGL视景体坐标系下的偏移。具体函数实现如下: 2 i1 L3 v$ A! } e
void CTextView::DrawListText() , A/ V! w0 l6 s) ?4 [
{ ; h& Z$ H8 D$ r9 M
wglUseFontBitmaps(wglGetCurrentDC(),0,256,1000); / Y9 r9 i1 _6 K2 t
glListBase(1000); . g4 V: q$ Y, o( T/ H: O0 G
glRasterPos3f(-5.0f,0.0f,0.0f);
( o1 B* x9 T8 p9 @glCallLists(20,GL_UNSIGNED
- F7 ]( W" a5 c4 B_BYTE,“Draw with List Text."); 5 y% r# S3 S4 G
}
* |0 V/ X7 C8 y$ M5 Z/ Z% {; f1 h
6 G! W, s8 F, v- ~wglUseFontOutlines
- x( E! E _* Z$ A. `) T函数显示三维文字
; V4 N# E) W; r8 X! q----wglUseFontOutlines使得OpenGL可以显示三维文字。它的用法与wglUseFontBitmaps函数大致相同,但是多了内插计算参数、字体深度、显示方式和装载字模的缓存四个参数,且只能显示TrueType字体,显示前应该先选择字体类型。具体函数实现如下:
! A- l6 b/ ?8 Svoid CTextView::Draw3DText()
' T; P: k3 G- u! J) ~{
* y# W: X/ p. e% K$ vGLYPHMETRICSFLOAT agmf[256];
0 y3 \; \5 R- ]6 e( k/ d// create display lists for glyphs 0 through 255
- Q0 l2 ^5 R0 q+ o$ V( r0 S( ?: S// with 0.1 extrusion and default deviation. 3 \0 ^$ s( y4 H: k& c- L
//The display list numbering starts at 1000
$ g8 K! {9 t1 X, u1 V* r: S) u(it could be any number)
& n5 Z& b G2 ?wglUseFontOutlines(wglGetCurrentDC(), ) `: L/ C5 D+ Q$ @) \: M4 m6 {
0,255,1000,0.3f,0.8f, WGL_FONT_LINES ,agmf); % K3 U0 q. q7 u# g
: H- ]/ i* u7 k3 J/ ~) e
// Set up transformation to draw the string
3 E$ X0 n: \1 w: c3 p+ w9 ]glTranslatef(-15.0f,0.0f,0.0f);
2 m/ \* u4 \1 Z+ P, g: z- ~' uglScalef(4.0f, 4.0f, 4.0f); $ q/ G# y* ?7 _* W; M5 l9 a, N* L! x
// Display a string 6 I/ F, ^6 E8 E( ^/ y/ R7 i
glListBase(1000); 1 {3 s+ T/ D w2 q
// Indicates the start of display lists for the glyphs
& w" ]( Y9 u' k1 z2 |9 C O// Draw the characters in a string 4 C6 c# v/ p' F1 I, T! z9 Z& @
" h7 M! V+ d: d% L/ \5 jglCallLists(26, GL_UNSIGNED_BYTE, 3 H+ D5 N4 K- M
“Draw outline list 3D text."); , l2 y' E& n$ [* G
} |