本帖最后由 shane007 于 2023-9-6 15:32 编辑 # k, U* J( i/ j1 x, d% i; ^
, S/ P: ^. ?, F* V3 m
以下是一个用来做参照的opengl显示字符串的代码,使用了freetype库。
) L. j: Z/ r: ^# R. {本代码经过分析以及改写之后,将取代游戏原有的显示函数。% Q. @" d- a8 n! Y. Z
* K! I( C5 S: ?. M* _2 G代码详细说明如下
0 Q1 b h( z8 g1 c$ a; l3 B J* g& O: Z
4 s& J8 D; P3 W% W) T" |( q* L# S- 在上述 renderString 函数中,我们有以下关键部分的解释:
: M5 P8 @! `% f* }: R' V; p( e - ( t J" g( m+ ~+ t
- glBindTexture(GL_TEXTURE_2D, fontTexture);:将字体纹理绑定到当前的OpenGL上下文。这是因为我们要使用 fontTexture 中存储的字形位图数据进行渲染。
. E5 j; k% V3 K: R) l8 j( u& q# Y
, I& |, K3 \- y1 p$ }# w+ N, K- glEnable(GL_BLEND); 和 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);:启用混合模式以处理字符的透明度。混合模式通过 glBlendFunc 设置,这里使用了常见的源和目标因子来实现源颜色与目标颜色的混合,从而实现透明效果。4 i9 l. d- ~8 o* L
0 S9 O [" C0 ^; a9 D+ U/ U: H- glBegin(GL_QUADS); 和 glEnd();:在这两个函数之间的代码用于指定字符的四边形顶点,以便在OpenGL上下文中绘制字符。" Y, n3 k X7 a1 S2 O; r& w
- 9 M! h3 Q- B+ C9 b; s" M
- for (const char* p = text; *p; p++) { ... }:这个循环遍历传递给函数的字符串 text 中的每个字符,然后为每个字符执行以下操作:
: ?" p: n) P" j3 T; R - - G: k5 R i' m2 M$ Y8 `9 b
- FT_Load_Char(face, *p, FT_LOAD_RENDER);:使用FreeType加载字符的字形数据,包括轮廓和位图信息。加载后,可以从 face->glyph 中访问该字符的字形数据。2 R& d2 q9 c% P6 k- \2 _/ u( ~
- 8 l7 V, K0 S% i! f
- 计算字符在屏幕上的位置和大小,包括 xpos(x坐标)、ypos(y坐标)、w(宽度)和 h(高度)。4 R8 l! b1 G" b0 C
- & [1 i5 J9 J/ m) g
- 使用 glTexCoord2f 和 glVertex2f 来指定字符的纹理坐标和顶点坐标,以创建一个四边形。这四边形将字形的位图数据绘制到屏幕上的指定位置。8 i+ e9 c, {$ U" W% i% R4 _8 t
- * n d" Q) V7 b& E8 A. I$ m6 b( A
- x += (face->glyph->advance.x >> 6);:更新 x 坐标,以便将下一个字符紧密排列在当前字符的后面。face->glyph->advance.x 包含字符的横向位移信息,>> 6 是用来将其转换为像素单位的操作。
- [* U, A* H" q( t0 z( d - $ K( v& q. n* \' q2 Z
- 最后,glDisable(GL_BLEND); 用于禁用混合模式,以免影响后续的绘制操作。
. p1 ]' ~/ n& o8 P - " a) p3 j. l* `+ Y/ e! O$ v
- 总之,renderString 函数的作用是将传递给它的字符串中的字符一个接一个地绘制到OpenGL上下文中,使用FreeType加载的字形数据和字体纹理来实现文本渲染效果。渲染过程涉及到字符的位置计算、纹理坐标的设置以及混合模式的启用和禁用,以确保字符在屏幕上正确显示。( u. a( S5 V% W
复制代码
8 E2 Y4 t3 }6 c- E5 V; V0 f0 _1 x5 ^. j/ u. ~
- 字形数据处理的关键函数之一。以下是 FT_Load_Char 函数的详细解释: K4 q3 A8 _& A ^
- ; D" d7 L* h2 d' a6 [ ^7 @1 Y
- 参数:" A( H) u8 K5 |$ V" a2 S
- ! G1 U" f& e1 Q* f( s5 [: a
- face:一个指向已经打开的字体文件的 FT_Face 结构体的指针。FT_Face 包含了字体的各种信息,包括字形数据。3 ?5 h/ x! W3 `8 b
- char_code:要加载的字符的Unicode编码值。您需要传递字符的Unicode编码作为此参数,以指定要加载的字符。
2 K$ L$ H5 P% k - load_flags:一个用于控制字形加载选项的标志位。您可以使用这些标志位来控制加载的方式,例如是否要加载字形的轮廓数据、是否要加载字形的位图数据等。; {% D9 P0 {7 m; e/ ^- D( x* c+ V
- 功能:
( D% c7 r/ c& b
8 w% g2 s$ a$ ^+ p/ @2 O3 x+ M- FT_Load_Char 函数的主要功能是加载指定字符的字形数据。0 A( S4 ^$ p9 D- _( L5 c
- 如果成功,它将在 FT_Face 结构体中的 glyph 字段中填充有关字符的字形信息。
+ D5 e ^3 k9 X9 y - 这包括了字符的轮廓数据、位图数据、宽度、高度、位图偏移等等,具体取决于传递给 load_flags 参数的设置。
3 W' Q: w4 \$ v( d! }9 p6 y/ C4 N - 使用示例:) h2 b# w3 G" U1 i- G) j1 P3 k: ^. [, [
# q8 L) l: j2 w/ }- c: f; D+ t* }/ @% X& o0 L
- Copy code, w* V2 V! l8 Q, G1 r. V+ Q* g2 w
- FT_Load_Char(face, 'A', FT_LOAD_RENDER);
: D+ z, W7 q. g" n2 U8 v - 这个示例会加载字体中字符 'A' 的字形数据,并将其渲染到位图中。FT_LOAD_RENDER 标志告诉FreeType要渲染字符的位图数据。加载后,您可以在 face->glyph 结构体中找到有关 'A' 字符的相关信息,包括位图数据、宽度、高度、位图偏移等等。 x" x0 v5 C- H. }6 |! G
2 R, @: F! I4 _* |5 o" A- T! n, s( I0 V- 错误处理:
; P# q5 ~3 u1 B - 3 E/ r1 n: Y; g) l! V
- 如果加载失败,FT_Load_Char 函数可以返回错误代码。您应该检查返回值以进行错误处理,并根据需要采取适当的措施,例如跳过加载失败的字符或终止渲染过程。0 h5 T! g7 L0 c' }
- 总之,FT_Load_Char 函数是FreeType库中用于加载指定字符的字形数据的重要功能之一,它使您能够准备要渲染的字符数据,以便在文本渲染中使用。加载的数据可以包括字符的轮廓信息(矢量数据)和位图信息,具体取决于 load_flags 参数的设置。这个函数在字体渲染中起到关键作用,以便将字符正确呈现在屏幕上。
复制代码 8 ]: P: `1 D6 H3 O R( L: r
9 X7 Z7 e, y. \+ J$ s代码
; q. \ f7 G: E! h0 g4 S. [/ k+ e1 I. M ?: f! H
- ; p' r9 m" \9 z, a
/ f: e! [/ |" b k
5 B! V7 ?! K3 e- #include <stdio.h># B# w/ u2 i# T. Z& l
- #include <stdlib.h>3 {: g( y; _( X
- #include <GL/glew.h>. s# G* S# f9 ~ h
- #include <GLFW/glfw3.h>5 \' k5 K2 i$ u$ j
- #include <ft2build.h>
7 y9 D R+ Z. }" Y; d - #include FT_FREETYPE_H
! j- B/ ?+ r" V) n$ Z+ u - # {" S+ g/ S a
- // 定义字形数据的字节数组, {% l0 q) U4 b
- unsigned char fontData[] = {
" B) ^: F: s: P. w9 e - // 字形数据的字节表示
- b( f4 Z- \; l o/ j - // 例如,这里可以包含字母、数字和符号的字形数据3 H$ V% r0 @; d1 I
- };& X7 J6 p9 y7 n
) l! b: J' j' Y# }; g1 _$ ~: f- FT_Library library;
- E) e4 x5 P# d) W - FT_Face face;
( B3 G8 c4 O8 r - GLuint fontTexture;
+ q/ u7 x' |) j9 D( L3 f& l' { - ( i' X4 c7 k8 v6 U7 O# ^7 T. S4 y
- // 初始化FreeType库和OpenGL
6 p6 `/ O1 y O/ i ~& i' u: S7 Z - void initialize() {$ E- {2 f* V4 S- |8 j
- if (FT_Init_FreeType(&library)) {0 F2 h5 ~7 j& n8 I
- fprintf(stderr, "Failed to initialize FreeType\n");. l; \# G8 o( m5 Y O5 T8 C
- exit(EXIT_FAILURE);
8 A$ M6 ?% c( w3 h7 u - }
5 J8 X9 u4 j, {
' C: h9 G; q6 f# O- C1 }- if (FT_New_Memory_Face(library, fontData, sizeof(fontData), 0, &face)) {) f: b, C3 H' G
- fprintf(stderr, "Failed to create a FreeType font face\n");
; k$ h$ ?+ ]1 A3 v- F - exit(EXIT_FAILURE);* x2 `- f P1 m4 I: v! f% H" X
- }. E8 ?% D- _" O8 @. d; z9 T, a
8 _* E5 x" K, Y& q4 n$ W0 z- if (FT_Set_Pixel_Sizes(face, 0, 48)) { // 设置字体大小
; s- I! i }% c' ~2 m - fprintf(stderr, "Failed to set font size\n");! R* J9 F; n+ F. B! g5 ~3 H
- exit(EXIT_FAILURE);
, k8 {; E9 h, z5 L - }
% F/ Q6 N* E8 \& s7 Q
' A& K- ^0 O$ l* N- glGenTextures(1, &fontTexture);
6 k3 v! j# U* S) ~0 r - glBindTexture(GL_TEXTURE_2D, fontTexture);
* i- L3 y1 {2 \+ i7 u/ w L; J0 L! \ - glPixelStorei(GL_UNPACK_ALIGNMENT, 1);- Z1 {# X1 Z. W6 d1 h
* J [/ H; A$ a0 G" ^0 T- // 将字形数据传递给OpenGL纹理' ~" l) ~$ D% [
- for (int i = 0; i < 128; i++) {
q- p+ @( \/ {6 |4 l - if (FT_Load_Char(face, i, FT_LOAD_RENDER)) {3 n* |2 h! h4 S. X+ t* }/ y
- fprintf(stderr, "Failed to load glyph for character '%c'\n", i);2 ~" c3 N- K4 ^* v* f
- continue;4 r, Q: e$ |4 V# I
- }
( c$ X: o. s( t7 ] - 1 A% T; ^2 r: z U% ~% T
- glTexImage2D(6 s2 L- N, X Y# M
- GL_TEXTURE_2D,
% u: p6 O: G( t* d5 T - 0,
2 R, {6 [4 l- u3 X' j0 a& n4 \ - GL_RED,
; _3 f) _# @6 {% N, I B - face->glyph->bitmap.width,
2 A c4 ]% t A: a$ i& t& a - face->glyph->bitmap.rows,0 f3 U3 I( K# b/ n" E+ J( r
- 0,
9 ^9 |8 h8 B. w+ E1 v0 V - GL_RED,1 S9 r/ z9 [" ? V3 F; `+ h7 h9 D/ P% B/ e
- GL_UNSIGNED_BYTE,- `' I0 @3 S3 f0 D/ ?9 I
- face->glyph->bitmap.buffer
# a- \4 Y: Z/ Y - );
, x* ^- y& U. X2 m1 r - " H, X( _0 r: m* ~- Q" c% V9 ~
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);2 [, B: v- ^6 i" c1 k3 M$ o
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
; F/ ~ l& Q5 ^8 G: P' f! y - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);: a6 |. ?# V; R0 o0 ?- {& ~& G! k' s
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);8 M3 ~& P" `1 N
- }8 b! q0 {: H+ G& \5 [2 `, W# j5 G
- }
7 W ]$ d) z# G4 A) O% K
: m! ~8 @% c. b- // 渲染字符串- N6 H3 n' y- \/ S5 Q. J
- void renderString(const char* text, float x, float y) {1 A& k3 H P) Q; @
- glBindTexture(GL_TEXTURE_2D, fontTexture);
. b( q( A7 A# R4 T# r
- d% u# D U6 J u- glEnable(GL_BLEND);
- `- R9 N1 L& e) X( ]# x9 S - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);0 ^3 s( t" |: @/ c" I; h8 F3 M
- $ _) E* k8 V4 i* t
- glBegin(GL_QUADS);
. R$ }7 y( W6 i+ \4 F% D* t - for (const char* p = text; *p; p++) {
6 m0 y4 Q+ b" }" Y9 U$ W - FT_Load_Char(face, *p, FT_LOAD_RENDER);
d: {2 D) W# V! V t8 L/ z
, u7 b2 V! B% C) |: o- g; O: A+ J- glTexImage2D(
5 R: W% W9 Y& ^ - GL_TEXTURE_2D,
5 @& D; ]$ ]* i9 j; y) |# s- g6 g - 0,* A4 s, g6 `$ M7 ?8 f1 o
- GL_RED,; b8 [6 t4 W/ s% q
- face->glyph->bitmap.width,
# e$ q0 [+ S5 k2 O$ g - face->glyph->bitmap.rows,
+ `" R6 r9 G9 S& Z7 \ - 0,
4 Y$ D n! ^& e/ P1 p - GL_RED,6 C: O9 g* b; W/ H
- GL_UNSIGNED_BYTE,
+ K6 R& m8 K3 h, r+ S - face->glyph->bitmap.buffer
6 w( f' e1 ?$ N7 [9 @# _ - );
* M4 M! h& @$ Q3 N+ K- g0 u - & s+ g* o# x& y& a$ v
- float xpos = x + face->glyph->bitmap_left;- T0 R) `# W) ]% }6 Q
- float ypos = y - (face->glyph->bitmap.rows - face->glyph->bitmap_top);
" d* o! \: k9 T) k0 F& q9 [
# g1 T' x$ a+ C- float w = face->glyph->bitmap.width;. ^4 r7 K! N* X: z7 F
- float h = face->glyph->bitmap.rows; p8 E. L% ^$ ~5 p# o0 {* }( G) I
- : a& g/ u0 o1 B% n' n* _( |
- glTexCoord2f(0, 0);
! M% f3 s b: z/ O2 y - glVertex2f(xpos, ypos);
2 ]% X7 N" \# k* _
, U) r" _$ J* c. X- glTexCoord2f(0, 1);
' P h: s, p$ d) p( v - glVertex2f(xpos, ypos + h);
4 c4 s* p. [; N
7 x2 D4 ?' g& M! l7 z# z; {- glTexCoord2f(1, 1);
: w+ [. ]6 j# g% u' E - glVertex2f(xpos + w, ypos + h); V/ y' [8 r3 h6 g( w
5 ~3 Z7 ~" v8 w- glTexCoord2f(1, 0);
2 f; |8 X5 ]/ p - glVertex2f(xpos + w, ypos);
( o: l- T3 X0 ~9 @: V4 V0 J
( g, a: ^2 u( I% c) u! B5 a- x += (face->glyph->advance.x >> 6); // 字符之间的间距: R% z: y' E T2 B1 w) z
- }
8 N0 C% a: d& O T - glEnd();
2 B5 R' }6 l6 K1 s1 U! y - % `. l( B& M7 C% K {
- glDisable(GL_BLEND);* V3 H) @$ B5 _/ A0 g1 @: v y
- }
4 F5 {" T- V/ f1 H$ h# X - D6 J; k' A" L. a1 @
- int main() {
3 r! X6 \ t8 d* ` - if (!glfwInit()) {
/ E6 i3 y1 F6 _; n - fprintf(stderr, "Failed to initialize GLFW\n");
! ^0 W+ B/ |1 c! @3 d - return -1;
5 J. S- {, w/ N9 O6 r - }/ {/ t" W- X- B6 k7 [( l. {
% [2 y' R! `5 t9 u) _! b- GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGL Text Rendering", NULL, NULL);
' U" R7 g( @/ B/ r' ~3 x - if (!window) {
5 K5 E$ I: E; `/ ]! Q - fprintf(stderr, "Failed to create GLFW window\n");% Q2 _# i2 n! X# \4 @: g
- glfwTerminate();4 P; [, i1 Y3 k$ [) C9 f
- return -1;. f8 Q" ~! Q% I" [4 w3 Y
- }
" f% m; y$ @& | u) c# |+ a+ c
: r) S# n8 M3 Y1 @ E4 ~- glfwMakeContextCurrent(window);
' g8 a2 f; L; o& T - glewInit();' H9 o' q3 a! L, s
" J0 x% A) i1 D5 o1 u6 a# B- initialize();, I0 |& ?* Z9 y; A$ M
3 \; G6 D1 t% ^* f& m }- while (!glfwWindowShouldClose(window)) {
! d3 h- a4 |- U8 Y" r1 H4 m - glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
3 j4 H& A3 g( u6 u' I+ w - glClear(GL_COLOR_BUFFER_BIT);: n$ r. b$ g2 b/ o/ ]
- 0 t, w1 D+ Q; d; O0 F$ X
- glColor3f(1.0f, 1.0f, 1.0f);, \1 S- P2 M2 D) ]& I1 D, z* Q! l, h
- renderString("Hello, OpenGL!", 100.0f, 100.0f);
; l* M1 l2 h7 r - ! J7 c g2 Z% I! A4 p/ k
- glfwSwapBuffers(window);
' j0 J# g4 s$ E2 I+ V2 ]4 h! L2 @ - glfwPollEvents();+ m' X: b) {( n& F
- }
9 O9 p+ J/ I; a- G2 s7 k% _/ a
' K4 f' W0 x! O/ l' T- FT_Done_Face(face);4 u# g0 v/ u6 k' _
- FT_Done_FreeType(library);
5 c( ?9 s( t" N3 W/ g* X - $ I$ W" N0 m3 ?* z4 B5 ^' i$ w
- glfwTerminate();# s; ^2 A y0 Q6 Y' E+ l( t" @; g
/ ] r/ `. R1 x: n+ T- return 0;
2 G; a6 j! ]; i - } O1 D) ]( Z, }( o! Z0 ?& c
复制代码 & ?% d" C1 R& d8 W( q0 O
9 C) t- r- _. W% o- T7 V O, c0 r |