本帖最后由 shane007 于 2023-9-6 15:32 编辑 # @) C2 W i+ c( ?- m _' {, N
2 Q, ]" M' K' E! @& ^1 x$ [以下是一个用来做参照的opengl显示字符串的代码,使用了freetype库。1 ^$ c" k% |. }
本代码经过分析以及改写之后,将取代游戏原有的显示函数。
9 q; ~3 r+ D0 S6 U, J
+ D( ~' `' i" v1 p3 e& F代码详细说明如下
/ t- e6 Q6 `1 R1 ^2 D) p9 }, D5 j& B+ A$ j; \
- 在上述 renderString 函数中,我们有以下关键部分的解释:
" P( f9 v3 w5 u: }4 n - * @0 A; C; N) k l3 X- t! _' X' W/ @
- glBindTexture(GL_TEXTURE_2D, fontTexture);:将字体纹理绑定到当前的OpenGL上下文。这是因为我们要使用 fontTexture 中存储的字形位图数据进行渲染。3 p2 s6 G# z4 _1 t
- / g6 m/ Z0 Z5 F- V% I5 w
- glEnable(GL_BLEND); 和 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);:启用混合模式以处理字符的透明度。混合模式通过 glBlendFunc 设置,这里使用了常见的源和目标因子来实现源颜色与目标颜色的混合,从而实现透明效果。' k) t& ~1 @$ j6 _6 E
9 i4 v8 p f9 z/ T0 c$ K3 y- glBegin(GL_QUADS); 和 glEnd();:在这两个函数之间的代码用于指定字符的四边形顶点,以便在OpenGL上下文中绘制字符。
1 a# R( N( ?3 d. _4 s" d& a
9 g p7 p4 c0 I, z) p- for (const char* p = text; *p; p++) { ... }:这个循环遍历传递给函数的字符串 text 中的每个字符,然后为每个字符执行以下操作:& f8 r. A, f7 n I: P" X! O2 ~
- 1 y; P) [# f$ y4 k
- FT_Load_Char(face, *p, FT_LOAD_RENDER);:使用FreeType加载字符的字形数据,包括轮廓和位图信息。加载后,可以从 face->glyph 中访问该字符的字形数据。( i5 ^, U8 h1 C- _1 o- w
- I, X, y" F6 ?2 B- k- 计算字符在屏幕上的位置和大小,包括 xpos(x坐标)、ypos(y坐标)、w(宽度)和 h(高度)。1 C' S. _0 C8 U) b, V- ]% O, B0 k
, x9 d9 a. v# U' p% N- 使用 glTexCoord2f 和 glVertex2f 来指定字符的纹理坐标和顶点坐标,以创建一个四边形。这四边形将字形的位图数据绘制到屏幕上的指定位置。
9 p, Z2 z7 { s! P
3 h D( M$ f' N+ s: ~$ T m- x += (face->glyph->advance.x >> 6);:更新 x 坐标,以便将下一个字符紧密排列在当前字符的后面。face->glyph->advance.x 包含字符的横向位移信息,>> 6 是用来将其转换为像素单位的操作。1 }/ G; ?) V9 Q, ?* t4 G3 l8 f
) u2 S. p1 S; L/ e- 最后,glDisable(GL_BLEND); 用于禁用混合模式,以免影响后续的绘制操作。' U0 s4 }, ]4 z! ?
- - k. J9 O6 U5 \9 @
- 总之,renderString 函数的作用是将传递给它的字符串中的字符一个接一个地绘制到OpenGL上下文中,使用FreeType加载的字形数据和字体纹理来实现文本渲染效果。渲染过程涉及到字符的位置计算、纹理坐标的设置以及混合模式的启用和禁用,以确保字符在屏幕上正确显示。
u, y0 Y) S5 G, E& {
复制代码 7 j: w4 B! z9 n$ u
1 h2 T/ K, B- ]7 g' W- 字形数据处理的关键函数之一。以下是 FT_Load_Char 函数的详细解释:2 Y( E3 Z1 l) o7 f, u' `
- " y2 C& [& z* K4 y( N
- 参数:
}8 i r6 K/ g( E( ~2 \ - I( ]8 ?2 s5 I$ w* o+ T
- face:一个指向已经打开的字体文件的 FT_Face 结构体的指针。FT_Face 包含了字体的各种信息,包括字形数据。
* v, m" ]" L0 T4 q' P4 r+ s - char_code:要加载的字符的Unicode编码值。您需要传递字符的Unicode编码作为此参数,以指定要加载的字符。
& I7 g7 \( U) L3 r0 { - load_flags:一个用于控制字形加载选项的标志位。您可以使用这些标志位来控制加载的方式,例如是否要加载字形的轮廓数据、是否要加载字形的位图数据等。
& D3 Y7 I6 \% Y; Y: g- I - 功能:
* u; i- a1 J6 `, Y# T3 y7 j' O - 6 Q0 X5 f2 V( Y. h) O9 ] ?' M4 M: u
- FT_Load_Char 函数的主要功能是加载指定字符的字形数据。3 x- G3 @% d% t) [$ i
- 如果成功,它将在 FT_Face 结构体中的 glyph 字段中填充有关字符的字形信息。' w3 W F8 u2 N! j
- 这包括了字符的轮廓数据、位图数据、宽度、高度、位图偏移等等,具体取决于传递给 load_flags 参数的设置。* \0 x7 W+ H5 `7 m: ^" E
- 使用示例:
9 J0 ?- D, G. M+ r
* M U$ R& k: c3 ^- B- c7 }+ J* }4 P: h6 |% q
- Copy code3 U" f9 C1 T% h/ R5 X- D0 R q! \
- FT_Load_Char(face, 'A', FT_LOAD_RENDER);
. H5 H- s/ i3 O' N9 V - 这个示例会加载字体中字符 'A' 的字形数据,并将其渲染到位图中。FT_LOAD_RENDER 标志告诉FreeType要渲染字符的位图数据。加载后,您可以在 face->glyph 结构体中找到有关 'A' 字符的相关信息,包括位图数据、宽度、高度、位图偏移等等。
( H8 c; V! X6 W0 f& f+ U2 U8 K - " a6 P* Z9 F6 o; W. {6 X% H
- 错误处理:0 n& F* L+ c: `" E1 l
- # M9 ^3 M+ T4 V U4 d4 I
- 如果加载失败,FT_Load_Char 函数可以返回错误代码。您应该检查返回值以进行错误处理,并根据需要采取适当的措施,例如跳过加载失败的字符或终止渲染过程。! r) e3 s; [8 L2 Y: j/ t
- 总之,FT_Load_Char 函数是FreeType库中用于加载指定字符的字形数据的重要功能之一,它使您能够准备要渲染的字符数据,以便在文本渲染中使用。加载的数据可以包括字符的轮廓信息(矢量数据)和位图信息,具体取决于 load_flags 参数的设置。这个函数在字体渲染中起到关键作用,以便将字符正确呈现在屏幕上。
复制代码 3 D' k# M. x# U% n4 g/ `
! x# A% ^# E3 r c8 [% k$ D0 [" C* Y
代码
, j, K9 ~9 ^0 l$ v6 e, ]
7 r7 a1 V' V9 a/ T$ k$ G
2 O; @. e9 ^, I
8 b4 N8 c& K" y7 g( N
|4 C! }( F6 c& b1 b- #include <stdio.h>3 W& O/ {/ `/ Y8 h% i5 E
- #include <stdlib.h>4 ]- t. U" R; [+ }* L& J1 `
- #include <GL/glew.h>
% S6 A; I) h- V/ ~. U - #include <GLFW/glfw3.h>
, ]: ?4 H) t) @3 L, E3 p - #include <ft2build.h>
3 K6 C6 b& {5 ?4 T* n8 e - #include FT_FREETYPE_H
n0 M+ T& \' v: A - % D! k" X/ d% G( s& _
- // 定义字形数据的字节数组
& a6 \3 b! e! q8 k! S - unsigned char fontData[] = {
! s! @: c, ?" D' E8 d - // 字形数据的字节表示
1 H. s9 g7 Q+ n, p - // 例如,这里可以包含字母、数字和符号的字形数据5 j" p5 ^. V* G4 H8 _
- };9 Q; e, K' W" e- a* z
9 g+ z7 d* j/ P2 b# Y- FT_Library library;
" b1 h: ]: K0 I5 _- j9 N' Z- j - FT_Face face;
$ `% N- }$ w/ F$ Z - GLuint fontTexture;
( o" D; G+ L% D/ v# k2 N( u - + R, r4 z+ `) J. o, g5 U, A5 }
- // 初始化FreeType库和OpenGL2 j7 k9 g4 P3 Q7 @5 s
- void initialize() {
3 \) v% Y1 d& \ - if (FT_Init_FreeType(&library)) {+ U! f0 c. i6 I/ U* H; e* s
- fprintf(stderr, "Failed to initialize FreeType\n");
* M1 k# }, D& b, w - exit(EXIT_FAILURE);2 J9 \4 y: |' |
- }
; V9 J- W! U/ [0 s; g - & t9 }2 V3 Y: k4 p
- if (FT_New_Memory_Face(library, fontData, sizeof(fontData), 0, &face)) {, u% O/ [% a* Q" c, H% _1 M3 J
- fprintf(stderr, "Failed to create a FreeType font face\n");2 l3 ?. Y4 b' Z( i1 C# @
- exit(EXIT_FAILURE);8 U5 D1 J4 S0 f: N. h- b$ F
- }1 D. e# }9 D' F
2 O4 P. |" X y8 L$ ?+ c- if (FT_Set_Pixel_Sizes(face, 0, 48)) { // 设置字体大小( t% g T/ w( ^: {& P
- fprintf(stderr, "Failed to set font size\n");
2 O9 `% O* g5 E$ k! b0 ^+ c* _ - exit(EXIT_FAILURE);" c: O% C) u: z( @ b# h! \9 F# m
- }
2 ?* B; b3 a+ Z% e. D - % n/ Y4 r% h. H+ {6 |/ R* @3 u) g
- glGenTextures(1, &fontTexture);' S) i' W( F* t
- glBindTexture(GL_TEXTURE_2D, fontTexture);7 O2 f6 K- _9 y7 n z
- glPixelStorei(GL_UNPACK_ALIGNMENT, 1);! I5 B: [7 W' O8 s
- 4 E; N6 Y: K+ ?( R; a2 C6 j4 G* u; _
- // 将字形数据传递给OpenGL纹理' ]& i' v( E# ~ P+ {1 m4 }2 u$ W
- for (int i = 0; i < 128; i++) {4 y3 i# B7 C! D
- if (FT_Load_Char(face, i, FT_LOAD_RENDER)) {4 d* e: V9 x5 W- m" F, h
- fprintf(stderr, "Failed to load glyph for character '%c'\n", i);
* r3 o/ m/ L& Z( E; C1 C - continue;: O V6 F2 h8 t# @2 w3 i5 Y# ], Y3 G" x
- }1 @# q1 \ g" T5 E% K( s
8 J' l7 [4 r! F; a# [- glTexImage2D(8 O$ |& ^3 Y( ^0 I2 `1 H
- GL_TEXTURE_2D," |9 d' Y! q ^1 b5 g* w$ W
- 0,3 F4 v! _& a, D$ R
- GL_RED,
- o" s( u% F5 A! s3 W - face->glyph->bitmap.width,3 S2 c" l( {8 T5 `
- face->glyph->bitmap.rows,+ H9 e3 ?4 Q2 u. Y# d
- 0,
: g; i/ N( F6 }0 c! o5 i* \9 u - GL_RED,7 d6 U$ \( C, Y6 O/ b9 }9 r; N- ]
- GL_UNSIGNED_BYTE,
( C* N# \- x) i - face->glyph->bitmap.buffer1 h5 C' L# Q5 V& G/ R: t
- );
2 s3 T3 Y6 r# x/ A o. k - 5 E8 p! l0 j& H4 g" |( y+ A5 Y
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
, Y; X. j. M! C7 A. R9 U0 R5 F - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ z. U; j2 {: Y - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
& ]& K/ I9 ]0 q2 Y& N$ Q - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
/ o& e* u5 b7 O& o% V: T- d - }4 A. P# \; l( p4 W6 d j* {4 w
- }
& v) |/ k H$ ^4 x - 2 f* E4 E! @2 I- Z' T
- // 渲染字符串
. @: W; J4 i& X. [ - void renderString(const char* text, float x, float y) {$ S" @: t, [+ C
- glBindTexture(GL_TEXTURE_2D, fontTexture);
! p' W l* z- X7 `) J7 \9 J
7 V- I" T1 t# ?9 J% l1 u- glEnable(GL_BLEND);* w; i4 W! Y& j( d2 { [
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- P8 {+ |+ I. ?6 R - 8 h& f3 X3 _) ?1 j0 R4 A/ ~7 [
- glBegin(GL_QUADS);
2 b+ D& W/ s3 S$ W0 p. J, W# n - for (const char* p = text; *p; p++) {" T" [4 h; g, ~5 r
- FT_Load_Char(face, *p, FT_LOAD_RENDER);3 h8 }; Y2 S/ A6 F5 s
- " j) f: v9 @; z7 X
- glTexImage2D(- l# i0 X* Y- z1 W& E
- GL_TEXTURE_2D,
" p5 X A1 H# p - 0,
- \/ }# [* c/ A - GL_RED,# `$ a4 ^) D2 ^ g, \, ] C2 g2 K
- face->glyph->bitmap.width,& B5 ~% @7 R# x4 n) M
- face->glyph->bitmap.rows,
) a5 c3 m$ Y1 T- _+ m8 } - 0,
4 @( ^4 L. E& Z6 s- X - GL_RED,6 r, X3 N$ ?( D! h! m$ ]" c2 g- [! o
- GL_UNSIGNED_BYTE,; }5 e9 Y, Y' B; A0 T) c/ d
- face->glyph->bitmap.buffer2 u+ u; J6 A* Q: F2 X: S
- );
2 \7 T2 {, w+ `. z4 r& D5 u4 ?; ], _. l - ! a4 f: v' l: c' F0 G' p9 x
- float xpos = x + face->glyph->bitmap_left;# V; Q/ C( t Q" ]! t }
- float ypos = y - (face->glyph->bitmap.rows - face->glyph->bitmap_top);
$ v8 F5 k X+ ]
; O* A. r2 ~' ~( k2 c* M, |4 K- float w = face->glyph->bitmap.width;
% P5 d& L/ O- Q) U* B - float h = face->glyph->bitmap.rows;
/ ?' d4 b c7 x7 k0 L" o# [
: K) P7 Z1 q1 U* l3 r- U- glTexCoord2f(0, 0);, {: U: {+ u' g9 j3 I( ]1 \( u
- glVertex2f(xpos, ypos);
! ]2 E0 Q Q0 b' |8 u
, j4 ]! t& O2 j! G- glTexCoord2f(0, 1);' ?! b5 v5 o4 G( N/ ^
- glVertex2f(xpos, ypos + h);+ _9 J8 ?7 h; y0 a- P7 M1 c3 l2 X$ L
- h$ D h5 A% P( }- glTexCoord2f(1, 1);) x5 C2 B5 ~" r; z; r( P
- glVertex2f(xpos + w, ypos + h);
' |8 m8 m* `+ ]7 I
/ {" F! f1 M* U' J; V! \- glTexCoord2f(1, 0);
) C; X# ~! A( Y1 w# B - glVertex2f(xpos + w, ypos);
7 q3 |2 x+ n6 S
7 `2 z+ S- W; F2 {2 e( w' _- x += (face->glyph->advance.x >> 6); // 字符之间的间距
* T& a& W( y, S. I& D - }
: s5 @9 n. d( G# q. D - glEnd();# `: [4 {1 A5 |5 r
- 2 ~+ x y. I. b
- glDisable(GL_BLEND);. @( v+ K. j- @# }2 s$ Q0 G( d
- }
+ D) u0 y" _5 o' l5 `2 c# c - / {; C/ u/ J& `0 y2 t
- int main() {
9 R/ x6 V) e& X. X+ g% _6 @ - if (!glfwInit()) {8 z* }( {' l5 c7 \' m
- fprintf(stderr, "Failed to initialize GLFW\n");
% Y" V# [+ b; B3 V/ i% m - return -1;
+ f6 o5 r- E* ]+ x& p - }
9 r# k. b: H7 M) N - ! z5 u3 ^+ F+ X1 ~
- GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGL Text Rendering", NULL, NULL);- G3 l+ J2 ] q% f
- if (!window) {
- Z& H, s, d: ~8 Z; M - fprintf(stderr, "Failed to create GLFW window\n");* a, m+ p! \, r! j
- glfwTerminate();; A# [# a' b4 i$ R* O+ e; N! d2 H
- return -1;
. ^( [ x# ]7 |& m5 Y% ?" P- ~ - }
, u7 x! v- s, o& c# N! R& @ - 9 e9 l; C, A1 x! ?! p3 b& ?% X
- glfwMakeContextCurrent(window);
3 u5 e3 t1 v2 ^7 r9 z - glewInit();" Z5 d2 ]' H7 f+ ^% [2 R; u. ?
, A+ _$ O( X! n e" ^6 E2 `9 x- initialize();8 P# P7 S3 V) ]5 N1 x% i% q! F
, N& W3 j+ Q& [& m0 S- while (!glfwWindowShouldClose(window)) {
, j* p! ^% k) r d( L - glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
J6 ^$ ]. j# z1 @4 z) F - glClear(GL_COLOR_BUFFER_BIT);) _0 G; m/ [- ?, G4 m
- , M- G/ `* V$ U" @3 N4 E
- glColor3f(1.0f, 1.0f, 1.0f);$ z/ w$ \5 J" | \& k
- renderString("Hello, OpenGL!", 100.0f, 100.0f);
! X2 T; B2 e# t - 4 N8 A* m0 L' W( p
- glfwSwapBuffers(window);2 o& N. R. T- n
- glfwPollEvents();* x1 h( h4 @+ M) j
- }
. ^. _, Z" s/ C! w
- D1 ] V- X( L/ Q. `- FT_Done_Face(face);+ l, i! h. O' K2 y: M' l
- FT_Done_FreeType(library);) ~% n; _ u3 K! u
- 6 A! D$ B' [$ I7 x4 @
- glfwTerminate();% J, A) E5 A; O0 U3 I" a
0 B. Y& Q- [+ Z6 M0 J! v- return 0;
4 n, a/ } Q3 O - }
+ C1 G5 w8 u5 y4 ?
复制代码
) \% y% ^- @9 C5 {) p/ p+ i" n
9 t3 `& `+ F: g( }4 |5 X ] |