本帖最后由 shane007 于 2023-9-6 15:32 编辑
6 d5 U& P% W% \0 v5 q; Z! M, a8 r5 \ j* V. n+ Y
以下是一个用来做参照的opengl显示字符串的代码,使用了freetype库。
! [' X# \) R) \5 t% C1 A& o F& }本代码经过分析以及改写之后,将取代游戏原有的显示函数。
& ~ E7 ^, e3 z$ e- ~
5 y& o( _: }* ?8 Q/ s代码详细说明如下
" M% |- v; {# T( p) v* ?% [2 ~! U8 `# r/ ]
- 在上述 renderString 函数中,我们有以下关键部分的解释:1 j* u* S% V q& w5 z; O
W7 a- M3 x! _1 V' k- glBindTexture(GL_TEXTURE_2D, fontTexture);:将字体纹理绑定到当前的OpenGL上下文。这是因为我们要使用 fontTexture 中存储的字形位图数据进行渲染。2 F1 f# e- \" a3 y5 o! Y! b0 f5 H
- : h& B! S- \ q1 f* Y
- glEnable(GL_BLEND); 和 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);:启用混合模式以处理字符的透明度。混合模式通过 glBlendFunc 设置,这里使用了常见的源和目标因子来实现源颜色与目标颜色的混合,从而实现透明效果。0 r0 m+ G( _% R8 e- o
- " e# N6 K3 c3 N4 J1 g# X
- glBegin(GL_QUADS); 和 glEnd();:在这两个函数之间的代码用于指定字符的四边形顶点,以便在OpenGL上下文中绘制字符。
+ P3 p1 B/ K, \
- A3 v; x9 D9 ^& Q0 Q! J- for (const char* p = text; *p; p++) { ... }:这个循环遍历传递给函数的字符串 text 中的每个字符,然后为每个字符执行以下操作:1 o' o* t* H, O' }2 b. i# f; J
- 3 Q7 q' h& s$ R4 `) _ k+ u9 l9 I
- FT_Load_Char(face, *p, FT_LOAD_RENDER);:使用FreeType加载字符的字形数据,包括轮廓和位图信息。加载后,可以从 face->glyph 中访问该字符的字形数据。: u+ | W6 f' x" l9 f: C% u
+ f# ~' {" f) _- 计算字符在屏幕上的位置和大小,包括 xpos(x坐标)、ypos(y坐标)、w(宽度)和 h(高度)。
- D! w+ I. a' H6 ~% I5 J) ^
, z' \* V" J7 w. E7 W/ a' B- 使用 glTexCoord2f 和 glVertex2f 来指定字符的纹理坐标和顶点坐标,以创建一个四边形。这四边形将字形的位图数据绘制到屏幕上的指定位置。
( @7 H( v- N0 u9 Z - 3 S0 C/ k& z( f! T+ J/ f
- x += (face->glyph->advance.x >> 6);:更新 x 坐标,以便将下一个字符紧密排列在当前字符的后面。face->glyph->advance.x 包含字符的横向位移信息,>> 6 是用来将其转换为像素单位的操作。. n$ j# `6 y1 k: i7 H
2 ?, [. b) k7 q1 o: F( N- 最后,glDisable(GL_BLEND); 用于禁用混合模式,以免影响后续的绘制操作。
; @% E& }0 f9 f$ J
# P9 J* s8 Y: M# s- 总之,renderString 函数的作用是将传递给它的字符串中的字符一个接一个地绘制到OpenGL上下文中,使用FreeType加载的字形数据和字体纹理来实现文本渲染效果。渲染过程涉及到字符的位置计算、纹理坐标的设置以及混合模式的启用和禁用,以确保字符在屏幕上正确显示。
3 g! y# Y7 O9 l% F6 E* X0 z# R
复制代码
1 x6 \2 v! |) m7 ?: E4 {$ ~0 p) q8 i' f+ \ x
- 字形数据处理的关键函数之一。以下是 FT_Load_Char 函数的详细解释: l( s# K+ o$ u) ^; a2 m
- 9 u4 }% V8 M5 z# Y- G+ f4 e0 A3 ^
- 参数:
* h: }) n9 g$ [& n$ B* |
2 G! I$ C3 w( N- face:一个指向已经打开的字体文件的 FT_Face 结构体的指针。FT_Face 包含了字体的各种信息,包括字形数据。
1 U' C {& x2 p: j+ f8 D - char_code:要加载的字符的Unicode编码值。您需要传递字符的Unicode编码作为此参数,以指定要加载的字符。, z1 u' x1 H5 r' V& B3 O8 o
- load_flags:一个用于控制字形加载选项的标志位。您可以使用这些标志位来控制加载的方式,例如是否要加载字形的轮廓数据、是否要加载字形的位图数据等。8 b7 A/ G' I& P2 C, j+ ]
- 功能:
0 {& h7 S8 N0 q8 b+ ?$ i4 E - 3 @9 ~3 N9 B8 Z, x2 _
- FT_Load_Char 函数的主要功能是加载指定字符的字形数据。
" x% H. x( M! d3 q. k, t9 w7 Y - 如果成功,它将在 FT_Face 结构体中的 glyph 字段中填充有关字符的字形信息。
/ H6 ^4 H* ?- W1 _ - 这包括了字符的轮廓数据、位图数据、宽度、高度、位图偏移等等,具体取决于传递给 load_flags 参数的设置。, @3 F+ f9 Y9 T
- 使用示例:
2 ~% b$ {0 f2 z+ ^3 t" v: d - 0 q( N( r8 p6 h0 T
- c' U4 Y2 a" v0 ^& B
- Copy code
w3 J' g' n7 ^) X - FT_Load_Char(face, 'A', FT_LOAD_RENDER);
4 q2 h8 o3 G# k - 这个示例会加载字体中字符 'A' 的字形数据,并将其渲染到位图中。FT_LOAD_RENDER 标志告诉FreeType要渲染字符的位图数据。加载后,您可以在 face->glyph 结构体中找到有关 'A' 字符的相关信息,包括位图数据、宽度、高度、位图偏移等等。/ ` p4 q) h& l7 n
- w* i6 a0 ~( B- 错误处理:
$ i- b+ v9 B4 h0 ^& |
9 H& `# w' @* X- 如果加载失败,FT_Load_Char 函数可以返回错误代码。您应该检查返回值以进行错误处理,并根据需要采取适当的措施,例如跳过加载失败的字符或终止渲染过程。
& ~! P q7 }* [( ~& p% B7 b7 f( f - 总之,FT_Load_Char 函数是FreeType库中用于加载指定字符的字形数据的重要功能之一,它使您能够准备要渲染的字符数据,以便在文本渲染中使用。加载的数据可以包括字符的轮廓信息(矢量数据)和位图信息,具体取决于 load_flags 参数的设置。这个函数在字体渲染中起到关键作用,以便将字符正确呈现在屏幕上。
复制代码 2 C& `2 _. _' n4 z* ?4 J6 l4 f
* U- o6 n5 f+ t* w# V* x( X代码
8 z6 _: u9 B4 {' S7 J. p7 \/ k: R& o+ A) ^4 I( c
% h) i* f4 M8 [6 H- 6 a! }1 ~5 d ^8 ?- {5 r& S$ ^' x
. y$ j6 y+ @% v+ \$ p7 z- #include <stdio.h>
: j" n2 j9 S) p5 a - #include <stdlib.h>. _: m0 t7 _6 F- Z
- #include <GL/glew.h>
* x) \3 r2 Y/ ]" d/ X - #include <GLFW/glfw3.h>
- Y2 }+ u# ] K4 B& L+ N$ r - #include <ft2build.h>
, V/ Y* p8 M$ \. H1 S% Q4 C, n' d6 w - #include FT_FREETYPE_H2 z$ ]* w, f0 E
- ' k! k+ E! D3 ^7 Z
- // 定义字形数据的字节数组
6 G* k5 n% \0 w) p- x1 v9 x% |. D% q - unsigned char fontData[] = {) R& ^7 ] n1 t1 f0 a" Y' _4 F+ c
- // 字形数据的字节表示
. R* O$ o% V2 [+ }1 u - // 例如,这里可以包含字母、数字和符号的字形数据3 ]- y' R, r4 v8 L8 z3 C* H; N
- };6 T$ d( S" s" H
' G) b5 X' r: A8 r. l6 j! w- FT_Library library;
6 ^3 E( U/ A% r/ R, z9 [/ Y: h6 A! z4 i - FT_Face face;2 U5 E! k3 o5 E7 L+ E% M
- GLuint fontTexture;
0 w( d( h# _ x/ U* i
' o: v. k! H1 ]9 j% D- // 初始化FreeType库和OpenGL
: O" n$ x8 K* t5 h - void initialize() {
' e$ G' C& G8 q* v5 G9 J - if (FT_Init_FreeType(&library)) {: `+ h7 a& C7 ?% w: j. x
- fprintf(stderr, "Failed to initialize FreeType\n");
. W' g+ R( [2 `0 }9 l1 t - exit(EXIT_FAILURE);
& h! I3 b8 ^, W- X - }/ R( f) ^" h# W
- % X# L( O% i6 n
- if (FT_New_Memory_Face(library, fontData, sizeof(fontData), 0, &face)) {
! ?* e2 i5 x9 ~; x4 |9 R7 }9 {0 {3 ?2 r - fprintf(stderr, "Failed to create a FreeType font face\n");) _! Q( ?6 [& F+ M: H% o
- exit(EXIT_FAILURE);
; [0 D+ m4 V6 b8 m1 A - }7 N2 U( g5 L: F) o @# ~2 U5 X( Z
- 6 I+ a% R+ }4 |% b5 p0 L+ @
- if (FT_Set_Pixel_Sizes(face, 0, 48)) { // 设置字体大小( K; w# _. J% M1 n7 ~# z
- fprintf(stderr, "Failed to set font size\n");( H3 o! n6 Z6 K
- exit(EXIT_FAILURE);
$ l4 G+ n; ], N" D3 u# v1 Y4 G - }
# Y) s: S. ^; c4 x
) Y9 B# k9 L- f- glGenTextures(1, &fontTexture);
+ F. g. o! h0 y2 p/ G* c - glBindTexture(GL_TEXTURE_2D, fontTexture);4 F5 @, g5 v& q* W- K# M" a
- glPixelStorei(GL_UNPACK_ALIGNMENT, 1);9 [) L, S2 G' Q2 s/ r3 f& ~
2 t$ Y0 ]# R% ^$ W5 V- // 将字形数据传递给OpenGL纹理6 l' C4 Y8 N6 D5 L" Y
- for (int i = 0; i < 128; i++) {. k. V6 Y2 s+ G
- if (FT_Load_Char(face, i, FT_LOAD_RENDER)) {: N/ h7 o5 t) r \
- fprintf(stderr, "Failed to load glyph for character '%c'\n", i);
* H8 N* W [: T% r# L( T. V - continue;0 Z+ i! {& |- R6 O
- }0 l. `4 d5 G7 e0 U( i3 l
+ o0 C$ K: ?) i; m; q, v1 ]9 R; k6 a# z- glTexImage2D(+ P7 ^. A; p6 b h/ B! `! @) N
- GL_TEXTURE_2D,* ~1 L) W/ Q8 i
- 0,+ ^ M: H+ k1 @9 r& r) q4 C
- GL_RED,
4 T! } o2 K5 {$ r4 M1 J8 T - face->glyph->bitmap.width,
4 f. J* j5 ^$ K; z( i% } - face->glyph->bitmap.rows,. c, U& j% w) v. h2 M/ V- r
- 0,
+ e, Q0 P5 y7 Q3 O) r - GL_RED," S6 O* e6 p! k9 T; c6 R; O
- GL_UNSIGNED_BYTE,; s, r* I8 b- g0 ~4 `; W L P
- face->glyph->bitmap.buffer
3 @+ ]1 L7 s: Z4 F% x( ] - );9 ^; ^3 R: H( S5 b/ R8 Z
* Z3 Q5 I( T; c# F1 u. q- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
: w- u6 q/ g+ g o n0 n - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
/ y! {: `7 h* H; g- Z3 ^ - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
: Z4 ?) C( o9 k9 e - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
2 y4 p$ b: T) K2 [# G+ V- c# \ - }
* p# q K* m% x! F - }5 Z% e4 m, w" t4 m/ Z
- 3 X- D; n' m1 i+ c( X/ H0 G$ V
- // 渲染字符串7 H+ e+ d. y" N, I, z
- void renderString(const char* text, float x, float y) {* ^0 F3 y" v) R% M$ Q
- glBindTexture(GL_TEXTURE_2D, fontTexture);& T5 e) D4 |0 Q9 ]0 b! u
, z/ |" n, Q9 K9 S/ T- glEnable(GL_BLEND);
3 `8 C c( Z& O* J, A- T7 R( x - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
4 @( x/ @. \. X9 ? - 7 C+ { C/ v: a1 I2 \( |
- glBegin(GL_QUADS);
( |, _$ ?9 M+ s6 C$ ?2 O' p - for (const char* p = text; *p; p++) {2 u$ N H% m+ u ?6 j& H0 J% I
- FT_Load_Char(face, *p, FT_LOAD_RENDER);
2 r, `# G0 H' `% y7 p3 F
1 u* A3 G1 c; L9 _# S- glTexImage2D(0 Z8 `, ^: B) u0 |6 ?2 d N r0 Y u- ]
- GL_TEXTURE_2D,
5 W# Z+ Q: Q' O% B* Z, L* Q' ^& ^2 ` - 0,/ Z0 N' n/ W6 C# s! @, e& |
- GL_RED,
& H% _ ] \. J - face->glyph->bitmap.width,( u J4 ^% i1 P0 ]* O& A! y4 }
- face->glyph->bitmap.rows,! J8 c& [/ r2 Q9 D, M% @6 Z+ t$ g
- 0,+ N1 P$ Q) e1 T5 R
- GL_RED,
9 I2 ?$ y7 Q% @4 N - GL_UNSIGNED_BYTE,
# L: m! M8 s% X; u! H - face->glyph->bitmap.buffer% a% R+ e, N+ o: P* v* r1 Q2 Z, H
- );5 s" u I- f7 e" _. F4 e! z$ a* T
- & ~8 W' u8 ^. m3 u
- float xpos = x + face->glyph->bitmap_left;
( k- \+ e2 c# s0 A* O - float ypos = y - (face->glyph->bitmap.rows - face->glyph->bitmap_top);( V; i9 i0 q5 V7 l
9 o, d; Z9 Z+ W/ ?$ }/ B) O$ a- float w = face->glyph->bitmap.width;( T4 r2 P! y @7 p
- float h = face->glyph->bitmap.rows;% v/ O/ ^ V- v1 O3 L
- ; {# P2 d# [# z
- glTexCoord2f(0, 0);5 ?0 y5 Q/ R8 F& U
- glVertex2f(xpos, ypos);! F- J, o v' c
- & t" d. b! E" c, G, ]% x, L
- glTexCoord2f(0, 1);
o( q3 |" i" L$ U& Q - glVertex2f(xpos, ypos + h);
0 p3 E* [& t# z7 F - t: [; ~% U; m1 {4 b1 H8 T3 O! z
- glTexCoord2f(1, 1);, v9 |1 \; b% u: c3 ]; F
- glVertex2f(xpos + w, ypos + h);
+ h6 M$ [( M3 \8 p6 ` - ) A# [" q. p/ h9 w) w. O2 |& k
- glTexCoord2f(1, 0);- X5 U0 X+ v8 h; t6 s" w. R
- glVertex2f(xpos + w, ypos);
" c+ v, ^: X7 n9 G
- ^0 }, Q5 ?% g+ C1 G* z: K% ]- x += (face->glyph->advance.x >> 6); // 字符之间的间距, a+ e3 t1 z' C! j6 h
- }. k) x+ G/ p& h* M$ }* O3 J! v' p
- glEnd();# X5 B% y( [5 A# L( X
- S" b& S/ E! j
- glDisable(GL_BLEND);( C/ f5 s# a5 J
- }
- s; C& m6 R0 ], H" L
* k: @" p2 n9 B# H4 c- int main() {: c8 F, i0 \$ _1 |
- if (!glfwInit()) {2 Y c" d3 i/ B+ f/ y
- fprintf(stderr, "Failed to initialize GLFW\n");
: q: ?- \- Q7 i* m" P% @ - return -1;
0 w; F2 K8 x, P5 W! f. D( } - }
4 E" C2 }/ x9 H3 H8 v - ) M& `9 _ _8 y; J+ O
- GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGL Text Rendering", NULL, NULL);
2 ^8 R- {% K0 T1 ~; B$ j: S - if (!window) {
( m/ n1 `$ H! w5 O, n3 {: m - fprintf(stderr, "Failed to create GLFW window\n");9 R! Z" Y: x5 f. N/ H
- glfwTerminate();' f, {( F. y; E% l/ w! W- B0 g
- return -1;5 T7 Q' [% b% I' d) ^( Z2 R
- }# P# e0 D' ~2 m l
% g- d1 u4 e" p* h) s- i- glfwMakeContextCurrent(window);3 O. V$ V" J( G! Z" c) y
- glewInit();
' Z) b$ }" h+ K' V# a8 ` - 6 R$ V; f. |$ j% ?* H3 Q
- initialize();+ ~) [8 [; x! b3 H6 Z
; q3 {0 {5 N- y" d& S- while (!glfwWindowShouldClose(window)) {
; A) F, p5 ?3 t& e/ s - glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
; F/ J6 a9 G2 a' ], H; p - glClear(GL_COLOR_BUFFER_BIT);
# O# c# D" o1 |! R! z" k3 w8 x
2 \; J7 E+ a$ t- glColor3f(1.0f, 1.0f, 1.0f);1 k9 O3 [( J# k& g$ M0 x& A/ S3 N: a
- renderString("Hello, OpenGL!", 100.0f, 100.0f);: _3 x- E |8 l* W; @. E
4 Y+ [8 B0 A4 t5 r- glfwSwapBuffers(window);5 |( @5 k% R3 u7 }/ v
- glfwPollEvents();
+ n3 ^6 {& y1 Q" @ t - }5 {' {0 L" I* w. _/ q& b
- 8 s. T! V# ]8 H6 Z
- FT_Done_Face(face);( X9 U) Q, u! J( W4 x* W7 ?
- FT_Done_FreeType(library);
9 p5 C# N7 T$ ]* r! y9 i. Q - + f5 K& B; |6 D* H+ p
- glfwTerminate();
3 H( H; S6 c, `' @# X( A5 r
8 B; V% r6 M5 W# ~; R% e2 @. |: M- return 0;# {% T) i# L: q6 I
- }. _8 U% n, q! ^9 s; y
复制代码
8 a* X, i+ Y! P# d, g$ c
5 O4 |* J: R- {/ W8 D |