本帖最后由 shane007 于 2023-9-6 15:32 编辑
5 D' C, q( S' @" k( S2 b# R; ~4 _5 a5 u2 W
以下是一个用来做参照的opengl显示字符串的代码,使用了freetype库。 _3 o$ M, a5 k! s, J5 i) F
本代码经过分析以及改写之后,将取代游戏原有的显示函数。
/ K9 d* Y: |6 l! }% ?# x" c) f! g, U7 S! a
代码详细说明如下; c2 @3 P" U3 {6 s
1 f! b* N8 {+ ~/ V- 在上述 renderString 函数中,我们有以下关键部分的解释:+ `! K* w8 n$ l% P0 q
- 8 _- U3 k( V) k9 A" h
- glBindTexture(GL_TEXTURE_2D, fontTexture);:将字体纹理绑定到当前的OpenGL上下文。这是因为我们要使用 fontTexture 中存储的字形位图数据进行渲染。/ w! m' U/ y" a. ?: m. z% q
' G0 Q7 R9 C! u, j, @" n0 y- glEnable(GL_BLEND); 和 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);:启用混合模式以处理字符的透明度。混合模式通过 glBlendFunc 设置,这里使用了常见的源和目标因子来实现源颜色与目标颜色的混合,从而实现透明效果。
8 h+ F! m) x2 \% L8 Z: d - 1 C" A2 N7 P+ q% X8 `$ F
- glBegin(GL_QUADS); 和 glEnd();:在这两个函数之间的代码用于指定字符的四边形顶点,以便在OpenGL上下文中绘制字符。8 j3 g( \! r& e* u' {" p7 ^
- $ S$ p8 D/ B& p2 S, V- [( s
- for (const char* p = text; *p; p++) { ... }:这个循环遍历传递给函数的字符串 text 中的每个字符,然后为每个字符执行以下操作:0 q/ b6 k6 `, m. d! r# T9 n
- 2 d1 z0 L% p+ \; ]" Q! S
- FT_Load_Char(face, *p, FT_LOAD_RENDER);:使用FreeType加载字符的字形数据,包括轮廓和位图信息。加载后,可以从 face->glyph 中访问该字符的字形数据。( x4 p2 N) O- Z* f( I4 l
2 J+ e. R0 }( u3 X" j; F* p- 计算字符在屏幕上的位置和大小,包括 xpos(x坐标)、ypos(y坐标)、w(宽度)和 h(高度)。 z0 d8 Q5 k) _6 U
- - ~, @# M0 k; M; P2 X
- 使用 glTexCoord2f 和 glVertex2f 来指定字符的纹理坐标和顶点坐标,以创建一个四边形。这四边形将字形的位图数据绘制到屏幕上的指定位置。
* z1 w# w+ x$ [ - 5 o( J0 |# }, K! n
- x += (face->glyph->advance.x >> 6);:更新 x 坐标,以便将下一个字符紧密排列在当前字符的后面。face->glyph->advance.x 包含字符的横向位移信息,>> 6 是用来将其转换为像素单位的操作。, w9 }+ H& x, N/ J
- % Q9 F6 ^) [6 V/ F% s3 C* T
- 最后,glDisable(GL_BLEND); 用于禁用混合模式,以免影响后续的绘制操作。- M4 I% q; \# A
- 8 l; U( U- o# o
- 总之,renderString 函数的作用是将传递给它的字符串中的字符一个接一个地绘制到OpenGL上下文中,使用FreeType加载的字形数据和字体纹理来实现文本渲染效果。渲染过程涉及到字符的位置计算、纹理坐标的设置以及混合模式的启用和禁用,以确保字符在屏幕上正确显示。
# ]* ^* b% c, c1 \* L: U
复制代码
0 }( I/ R! n5 A1 S( P+ Q" ~ z& C& n% |) H: n# C8 F% x- y
- 字形数据处理的关键函数之一。以下是 FT_Load_Char 函数的详细解释:
K+ ^; G+ L! t - 4 ^& O" ]: ?1 z+ Q+ _( F4 F0 U. D5 p
- 参数:
) A- u4 z$ ^4 Y |3 j" A v
* r5 U% w! o8 C1 v7 k) F+ @- face:一个指向已经打开的字体文件的 FT_Face 结构体的指针。FT_Face 包含了字体的各种信息,包括字形数据。$ w _8 p+ Y2 U5 N" b; v* m* I S
- char_code:要加载的字符的Unicode编码值。您需要传递字符的Unicode编码作为此参数,以指定要加载的字符。
+ _$ X% Q$ l- T- A/ T - load_flags:一个用于控制字形加载选项的标志位。您可以使用这些标志位来控制加载的方式,例如是否要加载字形的轮廓数据、是否要加载字形的位图数据等。* O% y$ a- J2 R8 e/ E
- 功能:/ r8 {) I" Z* B- P* M3 ]7 p7 `
- , z, y8 H5 Z9 ^+ T R" D3 k$ l. ]: t
- FT_Load_Char 函数的主要功能是加载指定字符的字形数据。
5 `- l9 ~' a7 g$ ~ - 如果成功,它将在 FT_Face 结构体中的 glyph 字段中填充有关字符的字形信息。
; R% {& i6 ?/ E+ Z4 v - 这包括了字符的轮廓数据、位图数据、宽度、高度、位图偏移等等,具体取决于传递给 load_flags 参数的设置。
5 q$ J- `9 P4 {/ t6 N - 使用示例:
: Q% f9 b& \5 [2 s# \5 P, c - 9 U% j$ L7 K/ p3 p6 T6 W
- c7 ^0 w' Y: P3 y$ w2 }% h
- Copy code
) h" z+ |! L ~ - FT_Load_Char(face, 'A', FT_LOAD_RENDER);
/ A, I; l0 I' J - 这个示例会加载字体中字符 'A' 的字形数据,并将其渲染到位图中。FT_LOAD_RENDER 标志告诉FreeType要渲染字符的位图数据。加载后,您可以在 face->glyph 结构体中找到有关 'A' 字符的相关信息,包括位图数据、宽度、高度、位图偏移等等。
, W2 j& ^4 p7 M( g6 O S6 a - # J4 T! Z& k# @7 F, ]/ k1 z1 ^
- 错误处理:
) I* w; o5 l; ~) C, R0 U; N. X; V
4 D8 F" ~+ u. \. b, e" C0 l- 如果加载失败,FT_Load_Char 函数可以返回错误代码。您应该检查返回值以进行错误处理,并根据需要采取适当的措施,例如跳过加载失败的字符或终止渲染过程。
! g5 T7 K0 L1 U' \ - 总之,FT_Load_Char 函数是FreeType库中用于加载指定字符的字形数据的重要功能之一,它使您能够准备要渲染的字符数据,以便在文本渲染中使用。加载的数据可以包括字符的轮廓信息(矢量数据)和位图信息,具体取决于 load_flags 参数的设置。这个函数在字体渲染中起到关键作用,以便将字符正确呈现在屏幕上。
复制代码
# t& @7 \( f1 y% ?& K- J; d
" C; q- E- T0 @; D( r代码
8 }$ R5 E D, j' v
g/ V; |- `! [. C# w, v6 E, P+ u9 G- 3 G+ ]/ q1 a) s
- 7 d5 Q7 B4 h# a( ]
: w: G! V- G3 Y- E: G! f+ j- #include <stdio.h>
! F1 b. b( h3 G3 O' X1 }6 g - #include <stdlib.h>
" f& P5 _ |+ D; e - #include <GL/glew.h>0 W1 {- N* l* e$ x" k6 F
- #include <GLFW/glfw3.h>* t% ?' `! {: e1 b
- #include <ft2build.h>
$ X9 W+ R9 i; L8 _ - #include FT_FREETYPE_H$ }. Z2 R, H& ~( K) l& \
- * V% }1 {; M _) @# K
- // 定义字形数据的字节数组
- E; z0 x d* E' e - unsigned char fontData[] = {
8 i5 s" J, A/ r - // 字形数据的字节表示
, ]5 `4 y2 a1 T) ? - // 例如,这里可以包含字母、数字和符号的字形数据
! r4 G4 V* L2 p" h9 { N2 Z0 ^ - };
# ]" s# _$ q! s( I: L - ' S% O% w( n3 h" W& V1 c- M
- FT_Library library;
( Y; g( v2 W% n3 a - FT_Face face;' E9 u- [# Q" T8 L
- GLuint fontTexture; U; M( f* p0 }1 Q# X' o- P4 ^1 \
, h, R. i4 ?7 \- // 初始化FreeType库和OpenGL1 p0 w, c' u( G; |% ~8 Y6 J1 l
- void initialize() {4 Q5 }% f. x$ a, F
- if (FT_Init_FreeType(&library)) {
/ X& A& L1 Z, A" l - fprintf(stderr, "Failed to initialize FreeType\n");
/ B" ] u$ N8 S. J. z) L - exit(EXIT_FAILURE);. D/ X2 n3 Y2 ~) j" U
- }
$ i( X( E9 ^9 S: v
) j- z4 }8 @" |# w9 }0 B. r# T- if (FT_New_Memory_Face(library, fontData, sizeof(fontData), 0, &face)) {
. e' {. b$ b, C1 c0 h - fprintf(stderr, "Failed to create a FreeType font face\n");+ C+ q6 s- S4 H! W" A2 I9 R$ A
- exit(EXIT_FAILURE);
/ C0 n. v# s( C- k- N# u9 J - }
# r3 @7 z6 Z/ T! h. V - ( B$ n0 J6 w, b: R
- if (FT_Set_Pixel_Sizes(face, 0, 48)) { // 设置字体大小
+ A; g5 z/ ~ N9 [+ B - fprintf(stderr, "Failed to set font size\n");; O& p" S- ^ U6 t% p
- exit(EXIT_FAILURE);
, _4 c* Q% u0 u+ t* O& @4 d' H+ F - }+ ?3 V1 [$ _, o) G
- $ d( t/ Z9 }. F) P1 L2 s
- glGenTextures(1, &fontTexture);
% L" b, c4 f7 V4 t& \ - glBindTexture(GL_TEXTURE_2D, fontTexture);
' L) p/ N3 A# L' t# h2 [' v" C - glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
3 `5 b7 R$ `+ s
" {+ Y) c, `& ]! l- // 将字形数据传递给OpenGL纹理
) x+ b4 R) m+ S! | - for (int i = 0; i < 128; i++) {- P `+ E; \. D0 v
- if (FT_Load_Char(face, i, FT_LOAD_RENDER)) {
# H, |& D+ A' z8 _' z5 Q* r - fprintf(stderr, "Failed to load glyph for character '%c'\n", i);
6 ]. q* f% s. e1 T: j - continue;) s4 U. O) d( J# o& I4 ]3 S- G6 x
- }
( ^* a& R( K# n( t5 `2 v - 9 h0 v6 W6 \7 n) C8 D
- glTexImage2D(
7 i$ ]. t) o7 @. |! X6 Z& J - GL_TEXTURE_2D,9 Z; }8 X& o; l
- 0,
: u& r+ _" I! ^; F8 ~ - GL_RED,
- ` ]* o1 z1 `. d - face->glyph->bitmap.width,
8 P; P* d1 U" e& E/ L - face->glyph->bitmap.rows,
" {9 l7 H* M* n$ X - 0,
6 ?- n; _2 a' b( {9 ]" d; t - GL_RED,: G, W0 o0 i/ A3 |: s. m. @
- GL_UNSIGNED_BYTE,# v% S" G' I) K0 M/ |
- face->glyph->bitmap.buffer$ a9 O* ^9 ^$ K) V" v# v- k" c6 m2 J
- );
% @, p6 Z# `; R5 J2 J* E1 `# L
' i0 m/ n! t" n% A- l2 S- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);. w4 O9 h4 V7 ]/ {
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
7 J( x0 r [5 A, X# }4 [ - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
7 e4 A+ T+ S/ m" X - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);5 {' c8 J/ Z5 @& m) e2 ?! n
- }8 @2 W% P0 Z d0 y) k
- }% O' ~! Q3 N( _/ j0 _
/ O+ N+ ]$ \3 g/ h7 t" [# e- // 渲染字符串
# H- }. O: R; W+ ^9 { - void renderString(const char* text, float x, float y) {
3 Z$ P% r4 V5 _; y8 }7 f/ d$ } - glBindTexture(GL_TEXTURE_2D, fontTexture);
* f, U5 k n- v3 X6 e" r - 9 M. {$ k& n% t6 u; Q
- glEnable(GL_BLEND);
( f+ s* ~, t- }9 {, E9 k - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
6 V' Y- T* L* {' g) P5 X7 u
0 i, d Y4 U; {( z2 Z/ \% u: ^# q- glBegin(GL_QUADS);
0 G8 u. `, t% d S - for (const char* p = text; *p; p++) {
' o: m+ a# D* T# }( q4 | - FT_Load_Char(face, *p, FT_LOAD_RENDER);
* q3 I1 S; |0 d: V. M - ) l& W1 x* x2 c1 w, f7 V
- glTexImage2D(# x& b% b0 A8 v: \3 k, q& u
- GL_TEXTURE_2D,/ {$ R8 q ~% s9 d% G5 j# k. T+ ]
- 0,- B- {9 z3 A. f5 @4 t& u, r9 b
- GL_RED,; K9 e/ ^% W% P3 D2 L
- face->glyph->bitmap.width,* l% Y) f: F v F- D
- face->glyph->bitmap.rows,
1 ~& e3 |+ D, z& z" N& b. G8 r - 0,
) t& X) s/ C$ Y( ~4 |8 k - GL_RED,6 l6 x3 w: X4 j$ I0 Z$ x
- GL_UNSIGNED_BYTE,
7 i0 s) L, q h- ~/ C1 L5 B6 G9 q `) Z0 q - face->glyph->bitmap.buffer' e6 f* I) p3 f6 t6 @( M
- );- ?9 y5 I% \* R' y0 i% y; C
( u e' T. D5 Y3 p" X6 q- float xpos = x + face->glyph->bitmap_left;
+ W6 H+ T* d0 c ^6 S! d. k$ b7 Z - float ypos = y - (face->glyph->bitmap.rows - face->glyph->bitmap_top);
& p. D) r* Z1 J; W* D3 N7 j& G, s
$ @+ b6 W" c! S8 r: j2 H$ |2 K- float w = face->glyph->bitmap.width;
) v% G+ b; n& L3 y, J& e - float h = face->glyph->bitmap.rows;' q1 n3 ~7 i0 t( r
- ' k4 \' o, Z6 v, I
- glTexCoord2f(0, 0);7 M( S( T" C8 T6 ]/ s
- glVertex2f(xpos, ypos);5 b9 x2 |; r6 Z6 h7 x' ]% @
1 w; M( L+ E( u4 s- glTexCoord2f(0, 1);
0 Z8 e' L! |! Z* h0 j( F. O+ b - glVertex2f(xpos, ypos + h);6 [, R7 A) C% z4 ~$ h% q
5 j' z- M% F7 t6 m% T- glTexCoord2f(1, 1);
V+ h" E2 G1 W$ n t. v2 p - glVertex2f(xpos + w, ypos + h);
& E' T# j" y* q6 {4 s - # v! N9 Y9 ~+ `3 f, o
- glTexCoord2f(1, 0);
x4 @) |) }2 y) I8 W6 i - glVertex2f(xpos + w, ypos); w: u2 ~6 ?- n2 ?+ o( t1 c1 M& }9 i
- " \% K( G* x; B, b; |5 O
- x += (face->glyph->advance.x >> 6); // 字符之间的间距6 }3 w m" N5 V' N6 E- n @
- }, H/ E4 L# K0 {! H8 ]9 _/ b
- glEnd();1 @. p H8 c# D7 z$ w
9 w! T" d% E+ \4 y* S. z Y8 J- glDisable(GL_BLEND);
% W# H! }( _: d7 T$ P: p% @ - }
( _! d1 m& d( `. h$ B4 } - $ M8 _" O+ Y: ~+ L
- int main() {
4 \4 E6 ~' ?) U' y8 k4 T - if (!glfwInit()) {" Q& z5 e6 M6 ?" i" }) G; s8 W
- fprintf(stderr, "Failed to initialize GLFW\n");# Q2 V; `0 u. o9 V5 x' D8 Z
- return -1;
5 u; R0 [ `* ^/ Z" C - }9 D0 V1 _6 U, p/ K: @- L
: q0 w, S8 j* o! n- GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGL Text Rendering", NULL, NULL);
- L& s) {% D- [, ]; N* s. j - if (!window) {* N# i- u! _3 L
- fprintf(stderr, "Failed to create GLFW window\n");2 T$ `( H/ q1 @. v2 M8 ^- I
- glfwTerminate();
( H! A6 v; F0 Z- v - return -1;" x5 x: j8 u+ W# I) x
- }
: o6 H! b' O* y4 W' g/ N - / d. \8 L, d0 l+ j- f; T) t
- glfwMakeContextCurrent(window);9 E! \2 t% `& \4 y
- glewInit();
$ S8 [1 Y7 W2 n) W9 b5 b - 2 X u% F0 Q* Y7 C. I0 [! f- v
- initialize();
$ K, ^+ \. t1 H9 n2 w
& E& W+ y3 W1 Y- while (!glfwWindowShouldClose(window)) {1 \5 L3 F" h. O$ K# H
- glClearColor(0.0f, 0.0f, 0.0f, 1.0f);& {' I$ q0 `' [
- glClear(GL_COLOR_BUFFER_BIT);1 s6 o; q. }9 T: Q3 }9 E
' k" [5 T: m8 l* x d- glColor3f(1.0f, 1.0f, 1.0f);
) j, h0 L- E+ p, R7 k - renderString("Hello, OpenGL!", 100.0f, 100.0f);% Q9 x0 d I0 a" h- h% o
- * e2 T6 g d* H3 `) k* I
- glfwSwapBuffers(window);
" i& r. I! e( C - glfwPollEvents();9 W j# e. U4 J/ ?6 g2 p9 F
- }
4 J8 w9 F) f7 p% ^, M8 F
, `# _! L' h$ a. P1 ^# k4 t4 D3 O- FT_Done_Face(face);, v5 z/ ^+ `6 b/ a
- FT_Done_FreeType(library);
9 I$ ~2 ]: O& C5 }, W& y7 z6 [
) b: }! r4 K' y- glfwTerminate();
6 ?3 [* C' E8 X3 Z2 ]* `" G
+ C. v H( p; T0 c$ C% ^- return 0;5 z, x4 f: \' ~0 _
- }7 T) `! L5 K% Q6 ?7 R% l& q' j
复制代码
# ]: N$ n! b& n/ f
) u/ B- T1 r3 z5 Y9 g |