本帖最后由 shane007 于 2023-9-6 15:32 编辑 8 K% ^9 k8 h/ B6 X1 k; b
7 z8 W) z X& t) ^* k' Y以下是一个用来做参照的opengl显示字符串的代码,使用了freetype库。
( Y' D* V. ^' r3 ]6 R* w本代码经过分析以及改写之后,将取代游戏原有的显示函数。& T; `3 S- Z+ }+ Z0 p1 q' w6 u2 C" U8 c
" R. {9 H, u; n+ q4 q( y( f代码详细说明如下
; M9 ^/ Q! O0 L1 V8 P
+ b% G& V' x+ q+ [2 `1 c- 在上述 renderString 函数中,我们有以下关键部分的解释:
6 [& R% @& E4 Z! [% [
( X* d7 `, H8 u% \- glBindTexture(GL_TEXTURE_2D, fontTexture);:将字体纹理绑定到当前的OpenGL上下文。这是因为我们要使用 fontTexture 中存储的字形位图数据进行渲染。* D& y, L* Q1 W
, K+ H( f+ L$ {1 k2 m- glEnable(GL_BLEND); 和 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);:启用混合模式以处理字符的透明度。混合模式通过 glBlendFunc 设置,这里使用了常见的源和目标因子来实现源颜色与目标颜色的混合,从而实现透明效果。7 n% H2 x1 U, r/ v' ]( N6 y
- 2 \( J$ N' G3 ]7 F
- glBegin(GL_QUADS); 和 glEnd();:在这两个函数之间的代码用于指定字符的四边形顶点,以便在OpenGL上下文中绘制字符。% _! r l) W' Y! o f8 v+ d
# S9 i$ x S3 c9 E- for (const char* p = text; *p; p++) { ... }:这个循环遍历传递给函数的字符串 text 中的每个字符,然后为每个字符执行以下操作:
X0 m, h, t2 u, Q
! p; S' M0 F, V3 a- FT_Load_Char(face, *p, FT_LOAD_RENDER);:使用FreeType加载字符的字形数据,包括轮廓和位图信息。加载后,可以从 face->glyph 中访问该字符的字形数据。
) g# ^* ^- u1 f7 k
2 r% Y: a$ h! F, z( s- 计算字符在屏幕上的位置和大小,包括 xpos(x坐标)、ypos(y坐标)、w(宽度)和 h(高度)。+ t$ u, ^; c! s) k. X+ O2 {9 I
/ ]/ N, t6 U. | j/ p( z5 u# c- 使用 glTexCoord2f 和 glVertex2f 来指定字符的纹理坐标和顶点坐标,以创建一个四边形。这四边形将字形的位图数据绘制到屏幕上的指定位置。7 w1 t, E( M7 ?) P; H
) m. z. x6 F* P5 S' M- x += (face->glyph->advance.x >> 6);:更新 x 坐标,以便将下一个字符紧密排列在当前字符的后面。face->glyph->advance.x 包含字符的横向位移信息,>> 6 是用来将其转换为像素单位的操作。" X* u+ e8 b! b: e% {$ |3 r8 i
- : _3 b2 H; B+ V1 B
- 最后,glDisable(GL_BLEND); 用于禁用混合模式,以免影响后续的绘制操作。
! t- g* y! z1 J, G0 y2 E - ) V$ y ` x, J: V: s' S3 S
- 总之,renderString 函数的作用是将传递给它的字符串中的字符一个接一个地绘制到OpenGL上下文中,使用FreeType加载的字形数据和字体纹理来实现文本渲染效果。渲染过程涉及到字符的位置计算、纹理坐标的设置以及混合模式的启用和禁用,以确保字符在屏幕上正确显示。
! s O: E0 b$ w- S$ c+ I
复制代码
7 {% [2 t7 g# |8 m0 m& p* z# B1 t! F$ o* z
- 字形数据处理的关键函数之一。以下是 FT_Load_Char 函数的详细解释:
" c5 Q0 `$ d1 s' Q6 v, M
( W! S3 \3 b; f% a/ S- 参数:0 B! v! r9 i1 T2 `2 f( i
- ' M' F; ^4 I" ]1 f
- face:一个指向已经打开的字体文件的 FT_Face 结构体的指针。FT_Face 包含了字体的各种信息,包括字形数据。
' n8 y" b& d) i3 y2 ~ - char_code:要加载的字符的Unicode编码值。您需要传递字符的Unicode编码作为此参数,以指定要加载的字符。
* X2 S! J; U9 |2 O/ O - load_flags:一个用于控制字形加载选项的标志位。您可以使用这些标志位来控制加载的方式,例如是否要加载字形的轮廓数据、是否要加载字形的位图数据等。
}2 |/ P. }+ F+ W! u" x* D - 功能:
# N3 ]" q/ R, ?& G2 M9 ~) T
+ X8 |' |) _( |/ p, [- FT_Load_Char 函数的主要功能是加载指定字符的字形数据。. Y. h6 i8 ]- U; B( ^ T1 m
- 如果成功,它将在 FT_Face 结构体中的 glyph 字段中填充有关字符的字形信息。
& ~8 [# ?, j. k0 o - 这包括了字符的轮廓数据、位图数据、宽度、高度、位图偏移等等,具体取决于传递给 load_flags 参数的设置。* e3 A( ~$ Y$ V0 z- b* C/ O
- 使用示例:
8 Y: A, q% J- U B( D; ? - : Y* E6 T( t3 e3 v3 ~, M
- c6 a# s# d$ T' g! D
- Copy code
+ H) Z! G6 H# Y4 m5 g9 R - FT_Load_Char(face, 'A', FT_LOAD_RENDER);: v$ C5 y4 G# T$ q( l- D' N4 u
- 这个示例会加载字体中字符 'A' 的字形数据,并将其渲染到位图中。FT_LOAD_RENDER 标志告诉FreeType要渲染字符的位图数据。加载后,您可以在 face->glyph 结构体中找到有关 'A' 字符的相关信息,包括位图数据、宽度、高度、位图偏移等等。 c! T3 H% k: X7 N- f4 | F9 B
- 0 E' B6 t1 k! @3 a% \
- 错误处理:6 ?' M2 T9 E B8 n' [
1 M( c+ l& I: S8 |- 如果加载失败,FT_Load_Char 函数可以返回错误代码。您应该检查返回值以进行错误处理,并根据需要采取适当的措施,例如跳过加载失败的字符或终止渲染过程。
! q+ i' Y# K) s. j - 总之,FT_Load_Char 函数是FreeType库中用于加载指定字符的字形数据的重要功能之一,它使您能够准备要渲染的字符数据,以便在文本渲染中使用。加载的数据可以包括字符的轮廓信息(矢量数据)和位图信息,具体取决于 load_flags 参数的设置。这个函数在字体渲染中起到关键作用,以便将字符正确呈现在屏幕上。
复制代码 " q/ [# H# p! u. i8 y7 ^4 m/ j
6 g; [4 f0 d8 M$ p2 f代码
( P T4 b2 ?) A, U7 v, D8 L
& X. Y. Y4 L; r( E- [; V+ d
! x6 Y0 R6 U: W2 Q9 G: W5 v) \
: s2 r/ U& F7 o- % G* z4 h5 f8 a8 M0 J, V
- #include <stdio.h>
7 f' ?+ @: ?+ p9 J - #include <stdlib.h>
" y' Y6 k& S5 O, K% L - #include <GL/glew.h>
, D% ], M8 h+ z6 J3 `6 M - #include <GLFW/glfw3.h>
9 l; O- r1 { ? - #include <ft2build.h>
1 `9 t1 Y7 G( ~ - #include FT_FREETYPE_H* q1 g0 u* U% R6 E/ N) s
- ; {4 ^* M' U- O; L
- // 定义字形数据的字节数组- \, Y6 ~9 |5 N4 H2 d
- unsigned char fontData[] = {
7 S& U% L( U. `4 j1 G- i, ` - // 字形数据的字节表示
& A, k H& o) f: [- v! T: } - // 例如,这里可以包含字母、数字和符号的字形数据
3 K+ l. C# F9 v" L9 P1 u3 D - }; L- k% g$ V8 L1 @+ w1 _
- , B7 Y _! k( }- z
- FT_Library library;$ P1 P7 h1 T, ?) m+ p
- FT_Face face;6 M8 E) t- Y7 c" C0 K- D
- GLuint fontTexture;
2 @3 H5 g1 U4 d* J
: s' W* M: w) |) Q3 @ x- // 初始化FreeType库和OpenGL% L! j4 i" z, K2 R1 G8 W9 Z9 K
- void initialize() {
! J" `7 p$ S( L/ g( J) Y5 V - if (FT_Init_FreeType(&library)) {
# m% M- |# _4 e - fprintf(stderr, "Failed to initialize FreeType\n");
1 J( |& D$ K$ [. M7 i7 G - exit(EXIT_FAILURE);
5 D/ w# T( ?; L$ \2 y - }
- H0 w/ ^* ^; l$ ~" B
* t; M, M0 r+ {% C# C# Z- if (FT_New_Memory_Face(library, fontData, sizeof(fontData), 0, &face)) {
, r e- {( L" I. Z! Q - fprintf(stderr, "Failed to create a FreeType font face\n");- t7 [+ @. q: D$ M7 L9 o! O- _
- exit(EXIT_FAILURE);0 X. ]# z" }' e8 ?8 g0 [9 C$ b2 O. F
- }
7 m" D7 v5 E$ K! O - , _3 X" _2 C7 I' r
- if (FT_Set_Pixel_Sizes(face, 0, 48)) { // 设置字体大小! Q, D. p6 X1 J" Y9 M% ?5 [
- fprintf(stderr, "Failed to set font size\n");
- j v. B; }% d& D8 A - exit(EXIT_FAILURE);
( }, j) m9 W2 u: M - }
4 \" v; _5 L5 G5 Y' d0 ^ - / P# }3 u5 U, F* L5 g; a. M
- glGenTextures(1, &fontTexture);
, t( M- `+ C( ^1 B, ^% E, ?- _ - glBindTexture(GL_TEXTURE_2D, fontTexture);1 N8 Q* E/ E* Q1 B
- glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
/ C& d- Y1 u5 P! j2 @
9 Y" A$ N. H" z Q4 m1 @% m3 w- // 将字形数据传递给OpenGL纹理8 l3 @* A- c+ ~7 j- K0 j
- for (int i = 0; i < 128; i++) {' Y! M: v& W1 c
- if (FT_Load_Char(face, i, FT_LOAD_RENDER)) {1 }; b/ \- u, e8 { M
- fprintf(stderr, "Failed to load glyph for character '%c'\n", i);
" L9 K0 }/ o/ ?4 ~ - continue;' ^/ K$ U" m2 x1 L6 K
- }
5 Z8 k, N; W' Z' I+ D - - U6 J. b% l f# y0 R2 y2 @
- glTexImage2D(
. _7 u* h% n( w6 a* a& x: y - GL_TEXTURE_2D,2 ?# ] {0 k, } U" J6 o5 I
- 0,
3 ?5 `4 Z$ K$ X: F6 n - GL_RED,9 V7 `/ y3 [, L/ q
- face->glyph->bitmap.width,
# ~& i1 U$ z: X d* u - face->glyph->bitmap.rows,( H$ T8 `8 j6 p; `0 l! {
- 0,% s6 X" I5 x" h2 B) E. c# o
- GL_RED,% g4 v p/ X3 S; H9 \; M$ H1 s
- GL_UNSIGNED_BYTE,: V0 P# h% Q* i
- face->glyph->bitmap.buffer
% V8 Z! z3 K o) s! B& Z& r - );
( L8 N9 a- ]& u9 v& R
6 n6 {. |8 D: ?- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
5 E- r1 r1 }0 z; T, c - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
6 v# H' b; e& U" E1 u - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);% d. A. l# } o8 {3 D8 K
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
, P. [" g% W8 M% i - }
2 E, D# w, z9 {/ ^) P& w - }
& O# w7 I/ D3 g$ }! @ - r' q' H5 z* t5 O3 j2 r- _
- // 渲染字符串2 r2 m/ W4 s9 G6 Y w
- void renderString(const char* text, float x, float y) {
7 Z" P1 y( {3 ~ - glBindTexture(GL_TEXTURE_2D, fontTexture);
8 Z, t# t# ?3 S \, S6 R! F! x - ) D+ u$ \9 n3 o5 c9 {
- glEnable(GL_BLEND);( q# [- g j* s: c. m0 q' s) Y
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
* c$ V1 r3 _* H# @9 {2 h - & E- T, x& X" b5 Z0 S
- glBegin(GL_QUADS);
# J/ ~ G! c6 x# V: M - for (const char* p = text; *p; p++) {) O* u& C) Q+ u, k6 n4 G0 L
- FT_Load_Char(face, *p, FT_LOAD_RENDER);, [6 R2 c+ Z( g% y6 W+ z
- 1 W2 m- ?+ x% E. L; A
- glTexImage2D(* }/ k8 y0 H* K
- GL_TEXTURE_2D,1 k% g: b7 t1 N+ a0 ?- H
- 0,* w5 w, E. ?# c" Z; V
- GL_RED,6 z% {& A% j) R' V0 t
- face->glyph->bitmap.width,. {% Q: {$ t0 ]* ?/ {: h
- face->glyph->bitmap.rows,
P) C& L+ I( L" F+ V1 ` - 0,; N' N; ?4 v! R0 z# y6 {& s8 A% g
- GL_RED,
$ l* u) {, g% Z7 x2 p - GL_UNSIGNED_BYTE,2 j* u& ^# I7 b) g) h2 _. V0 J6 y
- face->glyph->bitmap.buffer# @& |( i. i* U
- );4 t" @/ T4 K8 R4 e9 p
- ( Z$ N, R" ^7 ~- x7 |) |
- float xpos = x + face->glyph->bitmap_left;% k% l% t$ _& H- W0 R1 H9 L* i* D
- float ypos = y - (face->glyph->bitmap.rows - face->glyph->bitmap_top);
: ~. P& h; u! Z# D& x
% q1 w6 Y6 B6 k1 ?' b- float w = face->glyph->bitmap.width;
. x A- z/ s* G* ]4 j; M0 A4 ~" Z - float h = face->glyph->bitmap.rows;# G$ N8 U @+ n' O) \* o% X0 L8 y
- ! d2 l+ S7 P; X
- glTexCoord2f(0, 0);
* }6 h# D* ~: D c$ c% `/ g - glVertex2f(xpos, ypos);
7 o' |/ P# q3 c) u - " N% y x' I( _1 B
- glTexCoord2f(0, 1);5 @4 j) q$ h g9 S, Y0 [7 T+ _- R% m
- glVertex2f(xpos, ypos + h);
7 _+ E1 E4 j1 X; f
5 V0 b: s$ f4 m1 ^8 [- glTexCoord2f(1, 1);
' F1 y+ l+ t, m/ L8 {2 T - glVertex2f(xpos + w, ypos + h);
% b* a' r6 V7 z+ W! I2 b - ! }" F4 Z$ y' {; L2 ]( Q/ M
- glTexCoord2f(1, 0);
' K: _7 b8 O5 i6 k - glVertex2f(xpos + w, ypos);, F5 `$ V# f# u( @# [5 f
" g7 s! [2 q" h# A n2 L- x += (face->glyph->advance.x >> 6); // 字符之间的间距% o$ t! ~/ Z9 R; Q0 j$ l
- }
$ U, ^ z) V$ D$ q0 G# e2 V# k - glEnd();2 r: v' }0 I# {5 n, q% X6 V+ G
) {7 j3 h7 m8 k+ Y- glDisable(GL_BLEND);
2 ?# A& F l1 a* [4 o, K2 C, ^ - }
( q) ]0 q& X2 _. ]: Y8 z0 ] - " @1 k4 {: V' X8 m$ b$ o
- int main() {+ o9 O9 C( T( V/ a/ V
- if (!glfwInit()) {
# |; o. l2 }# f# j - fprintf(stderr, "Failed to initialize GLFW\n");
% I# d p% [' b$ k, t2 u/ p6 O - return -1;& B6 y% p# T0 a; t5 Y0 E
- }
$ v! i6 u2 k) S' z - 3 V( B; c. S# {5 m
- GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGL Text Rendering", NULL, NULL);1 {6 b6 e3 K7 o' c9 z( k9 J
- if (!window) { S" _2 k9 S6 Y" N) t J, Y1 Z
- fprintf(stderr, "Failed to create GLFW window\n");5 N. o* `# C; L; Y# |
- glfwTerminate();
/ d3 L8 d3 S) m. a; N- P - return -1;+ W/ g( E, C6 h( }* V" X5 g
- }# Q' ~ S; c& R, F8 d
- % p M9 g" Z+ |7 S# o9 V; P* Z
- glfwMakeContextCurrent(window);* Y8 G5 w5 _2 c* L# m7 P8 e
- glewInit();
* J" A) ^' T3 o/ S' q5 r" q - 8 t( @1 D" r5 V5 q/ b
- initialize();5 I4 a% k/ \4 F- l
) P- n1 U, Y; |5 {- while (!glfwWindowShouldClose(window)) {
) O) e/ t2 e7 T1 D8 q - glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
( J* J5 V4 c* Q3 ~$ E - glClear(GL_COLOR_BUFFER_BIT);* ?4 N/ X7 o1 n, r$ \# R
9 w! O+ ?1 W: t2 w5 k2 F1 [% z: G. g- glColor3f(1.0f, 1.0f, 1.0f);
/ a! g7 {% j, o0 B1 M0 S - renderString("Hello, OpenGL!", 100.0f, 100.0f);
$ G5 Y7 w% y0 S* S! A& t. v
0 A% W0 ]4 G, d& ?! V5 Z" \- glfwSwapBuffers(window);
1 V1 k, E+ {0 W8 ~2 a: r: y- g( A - glfwPollEvents();
. L, a8 g! z" ^/ o9 M - }1 J, k/ H% y4 N/ b
- 1 J) u3 u5 J# K. S- V# Y# J+ g
- FT_Done_Face(face);- L$ p7 A% |. q* v" f) @+ J6 D" y; _3 Z7 X0 ]
- FT_Done_FreeType(library);$ l1 L/ e. P7 { d3 S- D4 k4 n/ @/ v
$ ?4 l. J% ?% y1 v0 X6 R' n8 Z- glfwTerminate();
- f" b" i3 O" F# O2 O0 X - 9 O3 J( B o4 V- Y; l" O
- return 0;5 p0 o5 N( V# u0 G) C5 T7 R t
- }/ J$ Y) i8 M% |8 H
复制代码 9 \/ S% ~3 s! X* c0 |
$ y, v0 l/ x' P, D5 l* j6 {
|