本帖最后由 shane007 于 2023-9-6 15:32 编辑 $ l7 ^, s. z% R9 H( A/ A( L
]" v U2 b4 {: R# T5 z以下是一个用来做参照的opengl显示字符串的代码,使用了freetype库。0 y b$ @9 h/ O/ z) f
本代码经过分析以及改写之后,将取代游戏原有的显示函数。
0 T% [7 `8 E5 h8 N w
* ~0 h- U; r3 m8 A代码详细说明如下
9 Z% ~ {' K8 |* d: _5 _" ^& k$ Y) f5 k
- 在上述 renderString 函数中,我们有以下关键部分的解释:
5 n9 @+ C* N3 I2 l5 Z
! t2 q1 f( p- s4 ^4 g- glBindTexture(GL_TEXTURE_2D, fontTexture);:将字体纹理绑定到当前的OpenGL上下文。这是因为我们要使用 fontTexture 中存储的字形位图数据进行渲染。* x& \+ g9 T. |1 L9 A
- " L2 f) {6 k* d( e8 E3 X: [
- glEnable(GL_BLEND); 和 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);:启用混合模式以处理字符的透明度。混合模式通过 glBlendFunc 设置,这里使用了常见的源和目标因子来实现源颜色与目标颜色的混合,从而实现透明效果。
, y6 Z& i o9 l k
( E. p# `- r/ b8 U- W: r% R- glBegin(GL_QUADS); 和 glEnd();:在这两个函数之间的代码用于指定字符的四边形顶点,以便在OpenGL上下文中绘制字符。
; f: K# h) O- ~4 Q' U, A% [ - - S0 R/ t# u, P' a' N
- for (const char* p = text; *p; p++) { ... }:这个循环遍历传递给函数的字符串 text 中的每个字符,然后为每个字符执行以下操作:* V/ ~ W/ X; N- G
- * H3 m- K1 h8 P- r
- FT_Load_Char(face, *p, FT_LOAD_RENDER);:使用FreeType加载字符的字形数据,包括轮廓和位图信息。加载后,可以从 face->glyph 中访问该字符的字形数据。5 S* G3 Y" ^% p( S( x
2 [7 u# j8 g8 J" w9 e% u- 计算字符在屏幕上的位置和大小,包括 xpos(x坐标)、ypos(y坐标)、w(宽度)和 h(高度)。) f0 D& a3 c1 Y
- 7 O; M, Q4 U% N3 @6 t
- 使用 glTexCoord2f 和 glVertex2f 来指定字符的纹理坐标和顶点坐标,以创建一个四边形。这四边形将字形的位图数据绘制到屏幕上的指定位置。
* C: I" G% S( ]7 @3 D - - i7 u% c% m; U( u& L2 v
- x += (face->glyph->advance.x >> 6);:更新 x 坐标,以便将下一个字符紧密排列在当前字符的后面。face->glyph->advance.x 包含字符的横向位移信息,>> 6 是用来将其转换为像素单位的操作。
9 b* `2 v6 z4 B - ) o3 \* B Q, r9 y
- 最后,glDisable(GL_BLEND); 用于禁用混合模式,以免影响后续的绘制操作。
3 H; b) ~* o' \, Z3 a5 y - , Q8 e' P9 k' E0 I
- 总之,renderString 函数的作用是将传递给它的字符串中的字符一个接一个地绘制到OpenGL上下文中,使用FreeType加载的字形数据和字体纹理来实现文本渲染效果。渲染过程涉及到字符的位置计算、纹理坐标的设置以及混合模式的启用和禁用,以确保字符在屏幕上正确显示。
. G" y7 u1 T; Q: q+ W8 {+ n$ N
复制代码
0 k4 X' [" j |
* X* E: p8 D7 f- 字形数据处理的关键函数之一。以下是 FT_Load_Char 函数的详细解释:
: u5 R7 E# v; Y - 4 c9 w" ~* u6 E, u6 a! b: R
- 参数:
2 ~7 K |" q! b' e" F' y' P% i8 g, G
$ q& ^- E. N) X5 ~- face:一个指向已经打开的字体文件的 FT_Face 结构体的指针。FT_Face 包含了字体的各种信息,包括字形数据。
3 p! H- j. t0 [. W& E* d - char_code:要加载的字符的Unicode编码值。您需要传递字符的Unicode编码作为此参数,以指定要加载的字符。
6 N0 b6 K9 Q+ h" [9 y, ` - load_flags:一个用于控制字形加载选项的标志位。您可以使用这些标志位来控制加载的方式,例如是否要加载字形的轮廓数据、是否要加载字形的位图数据等。/ P0 s6 G" b8 O# o( y/ k
- 功能:
1 L5 m: H7 N5 d/ ~0 M7 |6 Y$ y - 4 S: D$ g! V& L0 r) ?8 x
- FT_Load_Char 函数的主要功能是加载指定字符的字形数据。: M, i x s$ c! a
- 如果成功,它将在 FT_Face 结构体中的 glyph 字段中填充有关字符的字形信息。" E; _/ t+ Q! E6 K& R
- 这包括了字符的轮廓数据、位图数据、宽度、高度、位图偏移等等,具体取决于传递给 load_flags 参数的设置。
6 m! \* r$ G$ i2 `8 P2 Q; m3 j - 使用示例:, O; `" ?" r6 m: M: u' N
- * Z6 k3 O* O& q4 B7 b
- c& d9 l% [$ j) ?: u4 Z% `
- Copy code
* h' P1 Y/ ~5 U4 ^% k - FT_Load_Char(face, 'A', FT_LOAD_RENDER);' P+ K8 L9 w b: ~9 s; g" n
- 这个示例会加载字体中字符 'A' 的字形数据,并将其渲染到位图中。FT_LOAD_RENDER 标志告诉FreeType要渲染字符的位图数据。加载后,您可以在 face->glyph 结构体中找到有关 'A' 字符的相关信息,包括位图数据、宽度、高度、位图偏移等等。
7 F3 a' s9 _" D/ y4 a - 5 E& `& H2 u9 ~, s5 r
- 错误处理:
9 u, v+ k4 ^+ A) @2 l
; v/ _, t" K: \1 ~% a4 u- 如果加载失败,FT_Load_Char 函数可以返回错误代码。您应该检查返回值以进行错误处理,并根据需要采取适当的措施,例如跳过加载失败的字符或终止渲染过程。
1 z$ M3 ?' i& C1 L - 总之,FT_Load_Char 函数是FreeType库中用于加载指定字符的字形数据的重要功能之一,它使您能够准备要渲染的字符数据,以便在文本渲染中使用。加载的数据可以包括字符的轮廓信息(矢量数据)和位图信息,具体取决于 load_flags 参数的设置。这个函数在字体渲染中起到关键作用,以便将字符正确呈现在屏幕上。
复制代码 5 E& C7 C6 m( c9 v% Q# M- V6 ]# L7 {
# [; ^: f4 H4 _: z5 F3 J3 _! F8 }' U
代码
4 t( z$ n+ o& ]2 x' R
. {" {2 g7 x( _, E+ Z- 2 _2 n# ^2 M. b: ?& p8 [
- 1 V4 T- c+ e# Y7 K
- ) L" {' H. R V3 z# [
- #include <stdio.h>$ ^- H0 _- A- M3 k
- #include <stdlib.h>5 C$ L' S+ @- g' c6 ?
- #include <GL/glew.h>
$ \+ M, `9 z) R+ K/ l% b% o6 J# I q - #include <GLFW/glfw3.h>: a4 u9 E; `2 N' \$ y7 S/ G
- #include <ft2build.h>' e8 d: S+ C6 \8 [/ V
- #include FT_FREETYPE_H2 w n3 R: y! K9 }2 g$ x
- 8 K# Z1 T, |$ ]0 s
- // 定义字形数据的字节数组6 u5 M' J' W' N3 g$ `
- unsigned char fontData[] = {
! \. B+ T6 _9 V7 P% c- L - // 字形数据的字节表示6 L+ f5 q. D6 i! w) L, @7 P( J
- // 例如,这里可以包含字母、数字和符号的字形数据7 J4 I- s, ]9 r7 T, I
- };
! L% V, ^ C6 O; D( h - / e! c- j# z: ]/ M$ d
- FT_Library library;
/ \) W c; {2 u5 x5 b3 L& Y- } - FT_Face face;
; {& ]/ }$ p1 y" D1 K - GLuint fontTexture;
# { k& y( x' ^) C
. m- H+ {: u& x; Z3 |+ e- // 初始化FreeType库和OpenGL
1 I0 S, r- n4 a% h. u' j - void initialize() {) a9 ] T0 g5 X! R3 {. G
- if (FT_Init_FreeType(&library)) {# j! \5 t9 Q' V; \
- fprintf(stderr, "Failed to initialize FreeType\n");* x- e: X+ X% M+ W9 s
- exit(EXIT_FAILURE);
$ b& _- \8 |2 W6 U T - }& h( ]2 @; j4 F- p& r3 c9 G
- - ~0 \0 m) q1 X6 U% F- d; j! n
- if (FT_New_Memory_Face(library, fontData, sizeof(fontData), 0, &face)) {, ]6 e; L9 r8 |' _1 _. D5 i0 V
- fprintf(stderr, "Failed to create a FreeType font face\n");
/ y/ C/ f6 N7 j% d" ?0 n8 H - exit(EXIT_FAILURE);
4 m, i. ^, S, L' W. }8 Y# d2 A - }
! ?2 ^1 M) f" y# C* Y1 w6 f - 9 \7 j' Z4 B6 S. e
- if (FT_Set_Pixel_Sizes(face, 0, 48)) { // 设置字体大小+ D8 _/ Q( ^; G' j
- fprintf(stderr, "Failed to set font size\n");. L( ?- ~( K# Q9 Y& A
- exit(EXIT_FAILURE);
+ _' B( d/ j, B& N3 O1 C; @' v - }/ b6 Q( {" {% Z' l9 e
- $ m- E7 `' h7 W
- glGenTextures(1, &fontTexture);6 M8 W: @& Z" _- H. ]! T3 K
- glBindTexture(GL_TEXTURE_2D, fontTexture);
+ C1 K- I$ S& q2 U - glPixelStorei(GL_UNPACK_ALIGNMENT, 1);, h, b+ X% E* J" n. ]7 v7 i& ]# S6 h; z) y
5 ~, T" [# v4 S! D- // 将字形数据传递给OpenGL纹理
9 x- ~' m' F" ^+ d1 |! L' ? - for (int i = 0; i < 128; i++) {+ r/ [: x% v8 |- y T; A" d$ V
- if (FT_Load_Char(face, i, FT_LOAD_RENDER)) {# R6 `$ ]: @: Z/ O( N5 ?* i" h
- fprintf(stderr, "Failed to load glyph for character '%c'\n", i);1 t* h4 M' A- @$ }# W
- continue;
5 K5 Z" S( u+ {+ @- e3 c" D - }; b* Y+ K8 K. D* k
) v5 E9 t+ H; i' J% z* h- glTexImage2D(
% j U8 l" D& F& Q3 e - GL_TEXTURE_2D,9 {1 M$ ] t8 N5 R# t/ Q# Y7 ]7 |
- 0,$ D, c" e k5 U2 S1 {! v
- GL_RED,
7 ]3 \ k. k+ ]+ e0 c6 V - face->glyph->bitmap.width,/ K% z5 `: d+ _% R7 Q9 ]
- face->glyph->bitmap.rows,
6 L% F+ `6 }% \2 k - 0,
4 V3 @ ?) |) F2 x" O! O - GL_RED,+ W9 L! U8 z! S% h% K- {% I( m
- GL_UNSIGNED_BYTE,# g2 B4 y( o3 Y1 a
- face->glyph->bitmap.buffer6 u+ `7 ^5 B8 @
- );
/ q& S; m. e' V# e: [& Q
) G0 B; I) w. l4 O) U+ w- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
0 n/ b9 r8 B9 i. I1 x! M - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
9 ^& z/ L: \* _ _ - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);+ ^; @' x: M7 p% y# x
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1 ?1 G' X. i. g5 o/ Y - }( D3 \2 e& \. E; \4 r' c/ q
- }
; r+ R7 v5 j+ Z `" L( Z0 I - # a( x' N& p, e. c; A$ F
- // 渲染字符串/ a7 v' K4 K( ?2 O. Q h
- void renderString(const char* text, float x, float y) {! A( T& I- K3 ~$ v
- glBindTexture(GL_TEXTURE_2D, fontTexture);4 v' d- W+ [* U2 C) N L9 `
- 9 T1 }9 P8 ]3 j7 |: n3 T
- glEnable(GL_BLEND);+ ~9 D/ \! S, @
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);( v. |$ i! l1 g9 r2 r
) N2 A" _5 F+ L' ]4 H L- glBegin(GL_QUADS);
3 Y2 Y2 B8 Q( B0 ?. Y! ` - for (const char* p = text; *p; p++) {3 {7 c" L7 }& i$ B
- FT_Load_Char(face, *p, FT_LOAD_RENDER);
& {3 x9 a O) A! P! u5 x2 s - " M& F f1 G/ C6 P! r
- glTexImage2D(
! s/ B" P, m- j* r3 A; M - GL_TEXTURE_2D,
# H& Y; p- K& @0 E3 v' o - 0,
7 L# a# R2 r% p - GL_RED,
p: b4 U' ]0 d. v* g: @) z) Z - face->glyph->bitmap.width,+ s& X. \# n" p) e
- face->glyph->bitmap.rows,6 M, _/ h! ]# N: P
- 0,0 o" B1 g+ \& D9 { E4 y% ^. U4 _
- GL_RED,
; \; \2 v! v# y! C. C - GL_UNSIGNED_BYTE,. K1 z t+ a) `
- face->glyph->bitmap.buffer
; ]1 [) r |6 J% G1 Q% v4 o - );- w5 D5 X1 |1 B/ d {
- / c+ U' l/ F* J/ B4 V4 b
- float xpos = x + face->glyph->bitmap_left;3 \1 B5 B2 n# ]6 V3 e9 ]
- float ypos = y - (face->glyph->bitmap.rows - face->glyph->bitmap_top);
. k. T+ Q, ?0 d) u. S! m! u- u - 3 R2 d5 i/ x! s- n1 H( }6 Y) f
- float w = face->glyph->bitmap.width;5 M" U- T0 \! o. j8 W; _' P7 I8 {
- float h = face->glyph->bitmap.rows;9 b7 ?5 i. R3 f9 N/ [7 L% H
& j. `1 b L2 g: y$ l# T- glTexCoord2f(0, 0);8 a+ D- g3 _. t) Q
- glVertex2f(xpos, ypos);: _; Z" q% A+ E
. H) c( r6 u; t1 j* _3 D0 E- glTexCoord2f(0, 1);% K, b' d1 ?0 f2 K3 m# G l; ~
- glVertex2f(xpos, ypos + h);
& S1 X# a W0 T0 G- L4 U& I - - |6 z' H, ~3 p( T( ^( O
- glTexCoord2f(1, 1);
9 {: v- f8 R3 h+ ~ - glVertex2f(xpos + w, ypos + h);$ C. @* z# i5 F
7 y% Y* {/ j* K7 z( m, U, y/ J- glTexCoord2f(1, 0);
( f9 Y1 R. O" T. I1 v" S" x; } - glVertex2f(xpos + w, ypos);+ q; [4 H; l1 H, J& a
$ ]8 e' O) W' ?- x += (face->glyph->advance.x >> 6); // 字符之间的间距4 H' w" X, v- ]$ z
- }
, p! L! D7 c% f/ [ - glEnd();/ o% d' Y7 |0 r9 o& L
- : o8 O1 N7 [3 U& N4 [0 F
- glDisable(GL_BLEND);
8 T- U9 H6 m) K; C - }
^& U2 K7 y& u& I L& _ b" G - ' O* j. ?5 m' ]5 A6 m3 B9 a
- int main() {
% Z, R4 Q/ X# s) G5 [, E- s0 O2 r - if (!glfwInit()) {# S5 ?/ p& a$ r+ Q. D
- fprintf(stderr, "Failed to initialize GLFW\n");
: B# Z4 a d" f( j - return -1;
6 u+ a: T) i# _# u - }
* z5 V9 n8 ?9 x M9 x - 8 G% J& G* T8 x: r
- GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGL Text Rendering", NULL, NULL);3 M+ h& Q B) }" q. Y# o9 X1 [
- if (!window) {( i: P+ b2 S" j/ x1 f9 x$ E' |/ C
- fprintf(stderr, "Failed to create GLFW window\n");; r, Q8 R! S, {' C
- glfwTerminate();9 Z P# r3 L, w3 h/ p; }& l
- return -1;
" T- C0 k1 w$ p - }
$ q, h/ l% }6 A5 U2 W; @+ m
7 V6 E7 Z' M8 j Z% b2 U4 a. ]- glfwMakeContextCurrent(window);
6 _3 X) `4 }" Q. ]. x& g3 l2 I3 c - glewInit();6 X+ p# X* J! N
- . i; P: H( n. S9 l, E
- initialize();9 z& O5 B$ Y/ [8 R) C' w5 s# Q
) D% ^+ i2 i D& u- while (!glfwWindowShouldClose(window)) {
" w% Y0 R8 w4 {! Q - glClearColor(0.0f, 0.0f, 0.0f, 1.0f);# C$ M+ D, n6 f! @ ^5 K( w
- glClear(GL_COLOR_BUFFER_BIT);- V, n. E3 b) ~% c
- ' w9 p$ K+ w9 p. \4 H3 R* F4 b
- glColor3f(1.0f, 1.0f, 1.0f);8 H$ s: l. s, P8 r
- renderString("Hello, OpenGL!", 100.0f, 100.0f);& W- f' X. o8 o e/ n/ Z7 o0 n$ Z
6 k% M0 V( _6 _/ p+ X" y3 z9 s0 e- glfwSwapBuffers(window);
$ ~& P# V) B. X( W - glfwPollEvents();
% z( p& X* w' n) V2 |8 t" p h6 g - }
, j5 _& Z8 J+ `, G3 A) [5 g4 d - # G h0 |4 N8 F
- FT_Done_Face(face);
: ]; [$ {' U7 I" h- w4 o; G - FT_Done_FreeType(library);
. O5 K; q" p; S, B$ ^
( l# ?( R; f4 L$ H" f- glfwTerminate();/ v, g4 i4 c4 e6 N
- 1 ^) B$ w6 I* X: `8 o% L
- return 0;' |, |( r V8 G5 y
- }
4 {# m& u8 G3 N* ?; [ l9 F0 T; m4 u. F
复制代码 9 v; L* Y& |/ l) ~. u$ `
# u+ n# X: S/ Z# K8 C" S+ q7 D |