本帖最后由 shane007 于 2023-9-6 15:32 编辑
- w6 _) d9 M2 S2 p" G A; r$ b7 o' M, ~- l4 i
以下是一个用来做参照的opengl显示字符串的代码,使用了freetype库。! R; I4 @3 A3 h' g% I
本代码经过分析以及改写之后,将取代游戏原有的显示函数。
4 ?/ [% |( z6 B; F/ f
, F6 M- ]7 f. s% Y! d代码详细说明如下! x/ \5 W/ P% |' m
|# v4 C# t5 Q' N& @' H
- 在上述 renderString 函数中,我们有以下关键部分的解释:0 X4 Y8 l+ G! ~
- 4 r) \2 c! ~" A( l
- glBindTexture(GL_TEXTURE_2D, fontTexture);:将字体纹理绑定到当前的OpenGL上下文。这是因为我们要使用 fontTexture 中存储的字形位图数据进行渲染。2 q" G' h, ~: @: \- l$ \
+ e8 V- o8 W5 O- glEnable(GL_BLEND); 和 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);:启用混合模式以处理字符的透明度。混合模式通过 glBlendFunc 设置,这里使用了常见的源和目标因子来实现源颜色与目标颜色的混合,从而实现透明效果。
) `- R8 S% _, ?9 h8 y
* f |! c' r/ x6 t5 j! c- glBegin(GL_QUADS); 和 glEnd();:在这两个函数之间的代码用于指定字符的四边形顶点,以便在OpenGL上下文中绘制字符。5 d5 i) G! ^* V7 p5 H3 X
- ; k* u6 D" h, T- }, H
- for (const char* p = text; *p; p++) { ... }:这个循环遍历传递给函数的字符串 text 中的每个字符,然后为每个字符执行以下操作:
: q9 r/ |+ o, q, B& l' D
! i Y0 i4 I7 t, M- FT_Load_Char(face, *p, FT_LOAD_RENDER);:使用FreeType加载字符的字形数据,包括轮廓和位图信息。加载后,可以从 face->glyph 中访问该字符的字形数据。( Y( ^: `/ F& L
- 9 n* J- ~. f: `5 T& a6 |
- 计算字符在屏幕上的位置和大小,包括 xpos(x坐标)、ypos(y坐标)、w(宽度)和 h(高度)。
- j# c: B* h+ r7 q) z# q - - x* {9 Q: F3 k3 H/ B: `
- 使用 glTexCoord2f 和 glVertex2f 来指定字符的纹理坐标和顶点坐标,以创建一个四边形。这四边形将字形的位图数据绘制到屏幕上的指定位置。% D+ b4 ]7 ^; A9 @+ D7 B2 ~
0 j' o4 `6 q2 {1 C- x += (face->glyph->advance.x >> 6);:更新 x 坐标,以便将下一个字符紧密排列在当前字符的后面。face->glyph->advance.x 包含字符的横向位移信息,>> 6 是用来将其转换为像素单位的操作。2 S0 R( B: Y3 c! r. o
- 7 H( V- G5 }/ u
- 最后,glDisable(GL_BLEND); 用于禁用混合模式,以免影响后续的绘制操作。
* N: _3 ~# @5 C+ C( K& T
% v- b0 N# I% j7 w! j, m- 总之,renderString 函数的作用是将传递给它的字符串中的字符一个接一个地绘制到OpenGL上下文中,使用FreeType加载的字形数据和字体纹理来实现文本渲染效果。渲染过程涉及到字符的位置计算、纹理坐标的设置以及混合模式的启用和禁用,以确保字符在屏幕上正确显示。3 Z. A/ O0 @4 X0 ?0 Q# b3 E
复制代码 0 O6 d. Z* ]. ~ S2 v( ?# e: E" X9 I
" h6 A$ ]& U2 a' e- 字形数据处理的关键函数之一。以下是 FT_Load_Char 函数的详细解释:
) T) w& z8 u$ O( ^. ]8 u - F, }% m9 u' P- l& R8 i) s
- 参数:3 t: q& W# c4 w8 Y
- ( l& U% W+ }& O2 e& Z" W7 l& {2 j% w
- face:一个指向已经打开的字体文件的 FT_Face 结构体的指针。FT_Face 包含了字体的各种信息,包括字形数据。
" V; X. N+ H$ K8 l - char_code:要加载的字符的Unicode编码值。您需要传递字符的Unicode编码作为此参数,以指定要加载的字符。
?# [9 v) N) L2 \0 H; Q% R - load_flags:一个用于控制字形加载选项的标志位。您可以使用这些标志位来控制加载的方式,例如是否要加载字形的轮廓数据、是否要加载字形的位图数据等。* I6 D: g) r" a/ _
- 功能:
. i1 p/ l3 `" G, V& y Q - ! g) H3 l+ n. @9 }
- FT_Load_Char 函数的主要功能是加载指定字符的字形数据。
/ w/ P% C: X% {: e: n - 如果成功,它将在 FT_Face 结构体中的 glyph 字段中填充有关字符的字形信息。
: o8 D& E6 e" f) q8 | - 这包括了字符的轮廓数据、位图数据、宽度、高度、位图偏移等等,具体取决于传递给 load_flags 参数的设置。3 }0 u, O2 K- r7 w5 b( h U
- 使用示例:2 c' @$ ^% C: q( Y: g$ w( p
: ]; d% ^8 p3 L# ?9 \- c
- \& \ ?7 V+ D - Copy code
0 S6 |+ a! ^# M+ V9 p( `5 X - FT_Load_Char(face, 'A', FT_LOAD_RENDER);0 v; g' ^1 m* y0 s2 ]. r
- 这个示例会加载字体中字符 'A' 的字形数据,并将其渲染到位图中。FT_LOAD_RENDER 标志告诉FreeType要渲染字符的位图数据。加载后,您可以在 face->glyph 结构体中找到有关 'A' 字符的相关信息,包括位图数据、宽度、高度、位图偏移等等。
# A% T0 z5 d8 r$ x$ T) }; ^ - ) F: a& ]7 S+ p$ y3 w! Z. _
- 错误处理: X/ U/ u" q: V7 S, \5 g: Z. ]' f
- ; S0 }$ U- k1 d2 c
- 如果加载失败,FT_Load_Char 函数可以返回错误代码。您应该检查返回值以进行错误处理,并根据需要采取适当的措施,例如跳过加载失败的字符或终止渲染过程。' @9 B/ n8 k7 a, o( d1 R
- 总之,FT_Load_Char 函数是FreeType库中用于加载指定字符的字形数据的重要功能之一,它使您能够准备要渲染的字符数据,以便在文本渲染中使用。加载的数据可以包括字符的轮廓信息(矢量数据)和位图信息,具体取决于 load_flags 参数的设置。这个函数在字体渲染中起到关键作用,以便将字符正确呈现在屏幕上。
复制代码
% I8 F/ y: Z" K/ \1 R# G6 k6 s" H& T- Q. e6 w2 b6 A+ A
代码2 K0 P, h8 i- g1 M' a
# p8 V* i8 ~! }+ l3 `. O0 S3 s- 3 |0 |# j- O. n) \% M2 R
V* G4 D: m+ l1 d- & o9 d) E! R* F1 e# F
- #include <stdio.h>2 p+ Z( h# D3 c K4 }0 x
- #include <stdlib.h>
6 U! k( P, y/ r - #include <GL/glew.h>
+ h o) v8 V! H3 Q. o0 \ J' q - #include <GLFW/glfw3.h>
3 M1 G1 U& [% G! r, v' V% w2 ]' ~ - #include <ft2build.h>
2 B% H; r1 f0 X; I, H' z3 w - #include FT_FREETYPE_H7 ~- _$ M, A2 q+ ~
- 4 m. K4 {+ J7 \, A% L
- // 定义字形数据的字节数组2 G6 P- B. q/ Z7 Y$ J
- unsigned char fontData[] = {
/ w* d' t& }. s0 T7 b/ L - // 字形数据的字节表示" t$ d, C) r% o0 s0 D$ }
- // 例如,这里可以包含字母、数字和符号的字形数据
0 M% A; c/ M2 F; o- d$ v3 ?- q - };
) ]+ y$ l) u( R5 Q" d3 V
1 j7 ~0 v2 Y& R2 m- FT_Library library;
Q0 c/ V8 s, ]( N+ m' p - FT_Face face;
. u! l# L* C; I5 h) e+ u2 O4 ?' s6 u - GLuint fontTexture;
. S, A& ? \; b/ T# O. R - ; \9 l: W1 \7 R$ g& `7 S
- // 初始化FreeType库和OpenGL. }* @- t$ w2 k5 l$ @( h
- void initialize() {
7 N; }8 S2 j n - if (FT_Init_FreeType(&library)) {; S' p8 q/ ?. y
- fprintf(stderr, "Failed to initialize FreeType\n");
% U) B# s7 ]3 { - exit(EXIT_FAILURE);
8 @2 F# b$ [% m' t6 A - }
; k4 T0 U8 Q- `- O9 O
_2 W* g& p# O- if (FT_New_Memory_Face(library, fontData, sizeof(fontData), 0, &face)) {
8 K" P2 }! U7 V$ Q( @ - fprintf(stderr, "Failed to create a FreeType font face\n");9 J. r: v8 o; Q6 D
- exit(EXIT_FAILURE);. W+ l' W7 F: e2 X
- }
! A, x6 ?# O7 T! ]1 A2 c - + w3 E$ }- C' d
- if (FT_Set_Pixel_Sizes(face, 0, 48)) { // 设置字体大小3 [+ a/ P/ D) l" \
- fprintf(stderr, "Failed to set font size\n");
" j* e9 I: Z' _3 ^8 ]6 p - exit(EXIT_FAILURE);; F8 t- Z2 g6 d T- `/ j
- }& [; G! j" A) c O
q2 E, _. N# t: ]! f3 l- glGenTextures(1, &fontTexture);7 M5 e! R6 F; b, E/ u! e# T
- glBindTexture(GL_TEXTURE_2D, fontTexture);4 s9 H4 k" [( J0 e2 {- n
- glPixelStorei(GL_UNPACK_ALIGNMENT, 1);! A! c3 ^' S4 J( Z! ?( Z
- L6 }- h( W+ y$ q3 t) P- // 将字形数据传递给OpenGL纹理0 m: @" N$ I& f6 D: j
- for (int i = 0; i < 128; i++) {9 E& I9 X2 M$ H& c( f2 J
- if (FT_Load_Char(face, i, FT_LOAD_RENDER)) {: U' b5 v8 @2 ^+ I! s. ?
- fprintf(stderr, "Failed to load glyph for character '%c'\n", i);: ^* a# d" e A0 a
- continue;
0 g( H' J- C! U$ f) K& Y; ? - }
* S6 \7 l" U( j0 D! w, x - 3 n& J# Z) ? q$ @" b
- glTexImage2D(
) u. S0 Y" b: ` y$ z# n - GL_TEXTURE_2D,
0 ]+ G6 c/ ^" G; B4 X7 m4 `! N - 0,2 c! x$ t- @- j' v8 W; }
- GL_RED,. K' e( ?" _$ l5 F5 s$ u+ J* g3 T
- face->glyph->bitmap.width," u3 w$ J1 n9 |" `. l7 C$ F
- face->glyph->bitmap.rows,
2 i6 i) V$ L* Q - 0,/ W8 x" K+ [' E4 A
- GL_RED,
. Z/ p7 p6 H! e1 q - GL_UNSIGNED_BYTE,. P, `( m/ M' n4 r: C
- face->glyph->bitmap.buffer
" n/ l, l7 Q9 { - );* L: t7 R v/ l7 z- L1 ]
) j5 U5 D3 \& X9 A- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
) h- V! M6 ]0 V% a) T9 I1 e' M - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
4 | p& g- T2 e8 W9 i( b( Z0 E- A. H - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); x0 C' n5 H1 w& r
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ F6 d L2 E x* M2 R4 L - }
4 A8 U$ ?# l L4 u' F - }
+ G! @1 R" m+ [/ E9 z* j0 a- W
; O2 @ s0 I+ q4 N- // 渲染字符串
3 L+ P- ]: e5 P/ w. Q& q2 N - void renderString(const char* text, float x, float y) {6 m& s7 e0 p2 y% k# U
- glBindTexture(GL_TEXTURE_2D, fontTexture);
6 w5 M v- g a
( b u6 s" T0 t i2 P/ G1 ~- glEnable(GL_BLEND);, i4 e4 z6 d9 F! T* X5 G, T
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
7 i" U. l5 b+ p/ r' J - & {5 C; J: j* I4 @& m' @- x
- glBegin(GL_QUADS);
* {# T; t8 i) C) _2 } - for (const char* p = text; *p; p++) {$ O% j( A2 o1 x
- FT_Load_Char(face, *p, FT_LOAD_RENDER);
7 A( f/ d' E3 \, z% E5 P$ i
0 m0 Z! A. z5 y) s% V- glTexImage2D(
b6 p. v" u* s9 G6 a; z8 Q9 G - GL_TEXTURE_2D,
1 N5 L3 K# C2 N( o' K3 d$ X - 0,
2 J; \* L3 Q7 f: `9 L- @6 }* c - GL_RED,! c: g, Y5 {2 r
- face->glyph->bitmap.width,
: b4 O* g$ I m8 K7 r- a+ S - face->glyph->bitmap.rows,: o* u4 W: i1 i# L+ M
- 0,3 F1 g8 p W8 b3 C! ~" T
- GL_RED,
4 z/ w2 w+ p/ B! s* o) X - GL_UNSIGNED_BYTE,
: i% H6 U2 k# ~5 ~! r$ k# D - face->glyph->bitmap.buffer, X4 b0 P, `2 G' b' f& z0 Y% _
- );
7 p; U2 p) ?9 N$ k& J - ; e! Q3 Q6 `& C
- float xpos = x + face->glyph->bitmap_left;
: J5 G5 u7 m4 Y F4 T- X. ?' }. N - float ypos = y - (face->glyph->bitmap.rows - face->glyph->bitmap_top);
: ^! R( w( z5 r0 u N; t8 d - ! S( Y0 X1 w# d+ K( w$ b" A
- float w = face->glyph->bitmap.width;
6 A8 ]9 i2 n9 X2 j* f2 \. \ - float h = face->glyph->bitmap.rows;
2 R; L, o2 A4 _8 L3 J+ W# ^0 l' { - ! z6 q- w- y; Y3 w( V( _- c+ j
- glTexCoord2f(0, 0);) T' Z0 j* z8 p4 Q. P0 u8 r
- glVertex2f(xpos, ypos);
4 m/ m1 w3 j) i& m; P& {# M& z - " X& v5 _ A/ c1 A0 v x
- glTexCoord2f(0, 1);5 d; X9 b i$ u+ g$ o: H
- glVertex2f(xpos, ypos + h);- U+ w2 x4 G3 g
- 2 x3 ]$ A! H, j. ?; L* [
- glTexCoord2f(1, 1);5 e3 t0 `- f7 _5 X
- glVertex2f(xpos + w, ypos + h);
; _; \" p i$ A3 | ?! X& L0 _0 \
/ N1 ]0 \4 t! d5 J; |) F- glTexCoord2f(1, 0);
$ |1 F o8 u9 ?! o$ X - glVertex2f(xpos + w, ypos);9 i' a: x4 n0 t& w
- K# l$ t" X5 E& n3 q, e- x += (face->glyph->advance.x >> 6); // 字符之间的间距
( y. c1 ?- j) T- d- G: n7 L0 [* h - }
5 {5 R5 p, S8 {$ e - glEnd();6 t. k3 J9 z: b0 l; O
- 5 G1 y' s$ Z3 a4 }
- glDisable(GL_BLEND);) @# P- ^+ K# r
- }% C+ [" i+ e1 @* h" t# w* e$ S
0 K% ~& X H9 c, m- int main() {9 T$ T+ t1 W0 Q) a- G, M: l
- if (!glfwInit()) {
. e- R& C' g/ M; Z8 q6 m - fprintf(stderr, "Failed to initialize GLFW\n");
@; y; X( Y" N$ p - return -1;
7 X1 C( p& _: r7 y - }
6 e( M5 t" p5 p2 j s, w+ w
" }* \ V1 Z- Q- GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGL Text Rendering", NULL, NULL);
) z" R1 L- O7 | - if (!window) {9 E5 g4 J z+ X+ D! N6 ]6 R! f& V
- fprintf(stderr, "Failed to create GLFW window\n");
$ i5 O, ]% H5 m5 F - glfwTerminate();6 M, {- C A" d- I% d
- return -1;: N# C5 }# f" V
- }
/ n, P$ M& d' q, [. z) u8 H
% k0 I4 X) ]# R6 r8 r- glfwMakeContextCurrent(window);
# C( A- p) z& h) s! F) w - glewInit();
+ j6 i# w" A# O- m8 t: n
& l3 I8 y- I H* y- U+ y- initialize();
7 M1 ?3 |# \' l
/ C+ f! g5 k0 v* l1 C8 W, c- while (!glfwWindowShouldClose(window)) {
6 ^8 A. a8 P' G6 `3 b5 y - glClearColor(0.0f, 0.0f, 0.0f, 1.0f);7 t/ Q- @7 A8 C' C% P% o
- glClear(GL_COLOR_BUFFER_BIT);
" m0 Y* @4 y4 m0 n - s: i* n7 J4 _
- glColor3f(1.0f, 1.0f, 1.0f);
6 J: A* K5 h* l+ f - renderString("Hello, OpenGL!", 100.0f, 100.0f);' W: y2 h8 H+ ^4 V
- 6 X1 B \* D' ~# {
- glfwSwapBuffers(window);
! }: I% c% S2 w4 [& t* D4 E% k - glfwPollEvents();
; O! j( _2 z3 [0 L - }5 }2 ~/ u* B3 Q1 O: m
- 6 e$ Q$ c+ c; y. i: R
- FT_Done_Face(face);/ P- ]7 L, i9 k% ~4 r2 [ g5 B2 W
- FT_Done_FreeType(library);
$ V! E* _, i Z! k } - ( `0 M) _$ A0 ^) \( k
- glfwTerminate();8 Z A; A: r/ R
- : t, m1 A: u1 A
- return 0;
C O2 `: [. e; v( |! O5 g - }
9 N2 l1 p6 Q- }# \8 e8 M/ O
复制代码
! F* Z1 S9 E4 Q- r" K
; }, [7 h* g; Z |