本帖最后由 shane007 于 2023-9-6 15:32 编辑 % V& B( f2 c0 p8 I/ \6 r/ F
* C+ S7 q. M7 U7 h: k- D6 e以下是一个用来做参照的opengl显示字符串的代码,使用了freetype库。' K+ Q5 j( e0 I8 `1 e" }
本代码经过分析以及改写之后,将取代游戏原有的显示函数。$ q5 V) r& B: L& x- }
1 l' R8 M' r% I
代码详细说明如下$ @6 ?3 i) M# k! W5 D
% n" s0 f+ ^* Y* H6 q$ i
- 在上述 renderString 函数中,我们有以下关键部分的解释:
+ g) v0 z0 r5 }( P - 3 b" b. c* d8 _4 B l @
- glBindTexture(GL_TEXTURE_2D, fontTexture);:将字体纹理绑定到当前的OpenGL上下文。这是因为我们要使用 fontTexture 中存储的字形位图数据进行渲染。6 r, L# M d' x6 L: A) i6 p: H
- % U) m8 N; u! B
- glEnable(GL_BLEND); 和 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);:启用混合模式以处理字符的透明度。混合模式通过 glBlendFunc 设置,这里使用了常见的源和目标因子来实现源颜色与目标颜色的混合,从而实现透明效果。
. m7 q% C- b8 J# C; c5 `' W5 r8 z - 0 ?$ |/ c% m1 e! o9 K+ D3 x
- glBegin(GL_QUADS); 和 glEnd();:在这两个函数之间的代码用于指定字符的四边形顶点,以便在OpenGL上下文中绘制字符。/ ^5 P% P. ` `
- # `, ?9 [' \ O- a! w* j) L( K
- for (const char* p = text; *p; p++) { ... }:这个循环遍历传递给函数的字符串 text 中的每个字符,然后为每个字符执行以下操作:) i! G+ D) e, X- E( h1 V' z
: V7 z0 Q7 g" L2 t! a. B- FT_Load_Char(face, *p, FT_LOAD_RENDER);:使用FreeType加载字符的字形数据,包括轮廓和位图信息。加载后,可以从 face->glyph 中访问该字符的字形数据。/ R2 X, N; \& n. |$ ?
) j2 P" s$ x7 K. _- 计算字符在屏幕上的位置和大小,包括 xpos(x坐标)、ypos(y坐标)、w(宽度)和 h(高度)。
" ^4 U/ P$ }6 ~* k. a - 1 l* Z5 ~: `# w A1 p
- 使用 glTexCoord2f 和 glVertex2f 来指定字符的纹理坐标和顶点坐标,以创建一个四边形。这四边形将字形的位图数据绘制到屏幕上的指定位置。$ h+ U, L: h, T( o
- * ]( t) I ^1 C! f$ J9 n
- x += (face->glyph->advance.x >> 6);:更新 x 坐标,以便将下一个字符紧密排列在当前字符的后面。face->glyph->advance.x 包含字符的横向位移信息,>> 6 是用来将其转换为像素单位的操作。; O5 m7 J+ q5 G/ \# {' d
- ) R. k- o; c* e
- 最后,glDisable(GL_BLEND); 用于禁用混合模式,以免影响后续的绘制操作。
% ?$ E( R- U7 K
1 a/ K/ I9 z5 m; R, Q# r( j- 总之,renderString 函数的作用是将传递给它的字符串中的字符一个接一个地绘制到OpenGL上下文中,使用FreeType加载的字形数据和字体纹理来实现文本渲染效果。渲染过程涉及到字符的位置计算、纹理坐标的设置以及混合模式的启用和禁用,以确保字符在屏幕上正确显示。
( {" b) R; k$ y, i
复制代码 a& M) i; b k
& O$ \, s( R: r$ I- p8 l5 G4 ?
- 字形数据处理的关键函数之一。以下是 FT_Load_Char 函数的详细解释:
1 w- s& H9 `' p* R3 Y - / W5 t$ w9 V+ ]) H* ~; b" P
- 参数:1 s% S. i9 b# k
/ C- |5 C6 z G9 B- face:一个指向已经打开的字体文件的 FT_Face 结构体的指针。FT_Face 包含了字体的各种信息,包括字形数据。
2 C- O: g* w0 |! s% U2 e - char_code:要加载的字符的Unicode编码值。您需要传递字符的Unicode编码作为此参数,以指定要加载的字符。- P6 L6 E8 c; _( Z5 o' `9 n
- load_flags:一个用于控制字形加载选项的标志位。您可以使用这些标志位来控制加载的方式,例如是否要加载字形的轮廓数据、是否要加载字形的位图数据等。
# d! h- ?! k) G; I/ [( I - 功能:; ~/ p3 K' r/ a6 F# e) X, ^6 n% Z
- 7 t6 B3 B$ m1 O1 }
- FT_Load_Char 函数的主要功能是加载指定字符的字形数据。4 U4 {/ D. p& D% F3 y4 c; }6 p+ z
- 如果成功,它将在 FT_Face 结构体中的 glyph 字段中填充有关字符的字形信息。1 g7 o3 @# w* Z
- 这包括了字符的轮廓数据、位图数据、宽度、高度、位图偏移等等,具体取决于传递给 load_flags 参数的设置。
' K. r! u Q- D9 `7 N$ U$ z - 使用示例:
7 X& O& q+ D* R* J* r
J& Q" C; ?0 h: ?' c- c' W$ ^; F2 j7 F/ d3 q" e% v3 k* r
- Copy code' [$ `6 @3 [5 y- G
- FT_Load_Char(face, 'A', FT_LOAD_RENDER);
. p$ R& @7 `' f4 s. T - 这个示例会加载字体中字符 'A' 的字形数据,并将其渲染到位图中。FT_LOAD_RENDER 标志告诉FreeType要渲染字符的位图数据。加载后,您可以在 face->glyph 结构体中找到有关 'A' 字符的相关信息,包括位图数据、宽度、高度、位图偏移等等。
e) G$ c4 Q: `6 X3 Q3 I6 ?; K
) Z' y& D+ C& b, j5 C8 E: X* W4 A- 错误处理:9 j+ V" y, s Y' V
- g# M$ z/ \; \( a1 r3 U- 如果加载失败,FT_Load_Char 函数可以返回错误代码。您应该检查返回值以进行错误处理,并根据需要采取适当的措施,例如跳过加载失败的字符或终止渲染过程。
3 O/ ^8 M" L; M# O- a& M - 总之,FT_Load_Char 函数是FreeType库中用于加载指定字符的字形数据的重要功能之一,它使您能够准备要渲染的字符数据,以便在文本渲染中使用。加载的数据可以包括字符的轮廓信息(矢量数据)和位图信息,具体取决于 load_flags 参数的设置。这个函数在字体渲染中起到关键作用,以便将字符正确呈现在屏幕上。
复制代码 ( L% g% U6 D0 b' x) S2 L
3 L$ V! w0 s: I: T3 `* \1 H代码/ V9 U" N5 l# M& D
9 G4 ?+ n/ {/ a, P4 Z4 H
& n6 D1 H- K# o+ F, ~% t
3 X/ k1 p. y% g5 p- ! G0 w z, j% f T
- #include <stdio.h>* y3 K$ o5 _9 }* ~# C j
- #include <stdlib.h>
5 C4 d: m: G0 @* L% H - #include <GL/glew.h>
) F) s5 v f+ G S5 q0 e2 \( B2 G - #include <GLFW/glfw3.h>
* N' \/ T! O5 t4 |5 x) N+ v - #include <ft2build.h>% K8 U9 h0 b1 `# Z; L
- #include FT_FREETYPE_H0 ?, I0 N' w8 R
- # Y0 i$ }" I, _' m) b* d' i, y
- // 定义字形数据的字节数组
: j- U8 a5 @; q& A( ~- i$ f$ o - unsigned char fontData[] = {
& y4 u4 ]6 w7 ~$ Q+ r - // 字形数据的字节表示
8 k0 }0 g8 n/ x% } - // 例如,这里可以包含字母、数字和符号的字形数据. Z# a- F/ F6 E# O7 M; S" c
- };
9 E3 s8 q) b/ A - / y' O( w7 e* K- D
- FT_Library library;8 i, ]9 z& e9 E1 C ?5 \7 V3 |
- FT_Face face;
2 r. K2 Y, b8 C4 N8 G: S - GLuint fontTexture;
0 w1 ]8 h6 z0 h: o4 x" @7 D' c' z - 5 G* k, h/ M4 B9 J- p$ O
- // 初始化FreeType库和OpenGL
9 w7 o, U: q+ p' H0 j& O G+ R - void initialize() {
+ ^: R9 U1 k- ^( t( t - if (FT_Init_FreeType(&library)) {
& Y# A0 s0 a C: E9 y - fprintf(stderr, "Failed to initialize FreeType\n");0 Y* d- Z% P9 _1 O4 V* b! J! C% R+ K
- exit(EXIT_FAILURE);
9 ~6 Y# f) j+ ?# Q3 B4 ?/ E - }
/ ?5 q. i- f, n2 c! j% O2 Z
2 P: O9 T; o& m+ J. K" c, i( Z/ P- if (FT_New_Memory_Face(library, fontData, sizeof(fontData), 0, &face)) {
4 A: |% p) D: b8 S( n/ v - fprintf(stderr, "Failed to create a FreeType font face\n");# U& A C# ~5 c- R! l+ M
- exit(EXIT_FAILURE);; ?+ }* x; c5 X2 h, ?
- }2 F, s* l/ O% m! U0 U0 U
6 s- ]8 G! |8 |) P4 y! K A. i5 H- if (FT_Set_Pixel_Sizes(face, 0, 48)) { // 设置字体大小
! i2 w3 s) Z) \3 Q9 g. A/ Y! W7 N - fprintf(stderr, "Failed to set font size\n");5 e- T* w/ Q2 k. ^$ }
- exit(EXIT_FAILURE);5 m' [7 e& r5 w
- }' g$ D' _% h& L" {& L
$ H! d8 T4 g: r8 L5 \ R- glGenTextures(1, &fontTexture);! i/ U% H7 F; D
- glBindTexture(GL_TEXTURE_2D, fontTexture);" N- T- S! i6 t3 I" m
- glPixelStorei(GL_UNPACK_ALIGNMENT, 1);7 P: r) l7 W4 Y
- 7 O1 ^1 X6 T( u$ w( B- ^
- // 将字形数据传递给OpenGL纹理( l- u8 H# W4 q
- for (int i = 0; i < 128; i++) {
- P) a$ W8 f" i* p' c - if (FT_Load_Char(face, i, FT_LOAD_RENDER)) {* T. ?' Q; y* g) |6 j/ H2 e
- fprintf(stderr, "Failed to load glyph for character '%c'\n", i);3 L2 a* o9 r/ G; p
- continue;& f C5 w; D4 y1 Z" a- `! X/ t
- }: o; Q) L& } g0 E
& |4 @* K& M d0 D, e$ K- glTexImage2D(
8 `) i" I" ^) b& \ - GL_TEXTURE_2D,3 \6 u8 u' C9 z. C, r6 K
- 0,
; S7 C6 n H6 ? c; i# m - GL_RED,
/ h/ l4 {( ?( f; J, D% v - face->glyph->bitmap.width,* p5 M }% E1 U2 _, }# p3 a8 O
- face->glyph->bitmap.rows,
8 M' k t& I7 H8 h$ x' e" m7 J% @ - 0,
$ `) v5 ~" t4 t, R( \8 r `8 p- l - GL_RED,
. X4 ~1 s. A7 I" o - GL_UNSIGNED_BYTE,
/ G$ O; N/ v! q& D0 m - face->glyph->bitmap.buffer
9 j5 Y! j' d U, k; b' P - );. F) `- T T1 r; F6 c1 ?0 J' k
+ U$ A3 P/ x7 |7 M7 [9 t% ^( L1 s- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
0 r x1 f6 J$ g - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
) e6 E9 K8 F1 f1 ?/ p9 Q6 H( O - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);& R* K# I5 X( a8 v0 g5 ~8 C
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);4 N& |5 B! O& s1 g# Z. `
- }. U" d' D* ~# i& w: t! u
- }' K% a9 I7 A6 s6 l) n# E- Q& v; N ]6 W- V
$ o: G. d" r7 E- // 渲染字符串$ h) |$ O% B# j" i
- void renderString(const char* text, float x, float y) {
2 a0 V' A, R2 {) k4 N ` - glBindTexture(GL_TEXTURE_2D, fontTexture);! a" A$ j/ m+ \! H7 P7 V; C6 H$ A
# J& L+ d0 J! X. m/ T- glEnable(GL_BLEND);0 q. ~1 `& c% {. R6 d
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);: \3 k3 W! l3 Q! m* |4 E
- T' u+ o; M) y, T; R- glBegin(GL_QUADS);/ @8 x: [+ [* U7 _4 J: C# r; y7 o
- for (const char* p = text; *p; p++) {
! K4 o1 |! @7 Y! M0 T% }3 p# h) b' d - FT_Load_Char(face, *p, FT_LOAD_RENDER);, K# S5 x/ u- q+ F' o; s
- 9 W' ]% c7 G' F$ Q' { W( f5 x$ q
- glTexImage2D(
1 R, j4 g3 `$ u - GL_TEXTURE_2D,
$ n% Q) _, a1 R& g - 0,/ a' o7 t- j4 s9 R8 d8 e$ U& e& T
- GL_RED,
% W: O, H9 V% U - face->glyph->bitmap.width,6 |" X- F- D6 V7 b: p6 W4 y; S
- face->glyph->bitmap.rows,
. u* [1 m8 ~0 L& [ - 0,
7 E8 ~* y# P7 F1 z - GL_RED,
9 T0 L3 y& Z( Y0 U8 V# | - GL_UNSIGNED_BYTE,0 Z! l. p. v' x, B' b( f6 B
- face->glyph->bitmap.buffer
2 P3 `2 y$ D" {8 ~6 J - );
& o2 v/ t0 h* |) I, C: s. }( E0 n - 8 e9 E# v! T# r' l% k
- float xpos = x + face->glyph->bitmap_left;
T! Z0 I$ J# j3 {% T - float ypos = y - (face->glyph->bitmap.rows - face->glyph->bitmap_top);) S/ Q: O0 i1 b' P! D& n
: H( Z) I. {) c& s- float w = face->glyph->bitmap.width;8 N% c6 x- I W4 ` M3 i- V' M2 P
- float h = face->glyph->bitmap.rows;
7 g4 h1 n% d) K2 E( u
2 F7 D B9 E& o8 [- glTexCoord2f(0, 0);5 W* F3 V' ?, |* ]6 q
- glVertex2f(xpos, ypos);
; q0 r/ Q% l5 ^: O6 @' j - " i3 `6 C3 d8 a, Q! F! a
- glTexCoord2f(0, 1);
7 `0 m+ _ `4 G3 k" F( O - glVertex2f(xpos, ypos + h);6 k9 \- y- v! m! B% u. |- C
- 3 Z; K* F, ~# Q4 U8 \5 o
- glTexCoord2f(1, 1);* w; O v; j5 I
- glVertex2f(xpos + w, ypos + h);/ N; s% c3 \3 C- m/ G
' Y: D. q# i* C9 J" g- glTexCoord2f(1, 0);
8 z8 w3 h) V4 f& f- l8 E {5 l - glVertex2f(xpos + w, ypos);
4 n/ w* U+ l. B! E5 i0 O
" L0 ?" ~ A R9 g- x += (face->glyph->advance.x >> 6); // 字符之间的间距
" z6 k, z( Q `/ J - }& ?6 t+ @7 v4 [: b" P+ ]+ p
- glEnd();
4 ]* W5 F! r% P. Y: ^- Y. w% b
$ J' |' Y7 z+ p4 @8 A) K+ n' L) m8 ^- glDisable(GL_BLEND);. }- u( g" b i+ I G
- }% i# Z5 Z% z' H
- 7 Y+ }1 q5 S- g5 W
- int main() { x; R' i' [5 x3 a' j
- if (!glfwInit()) {1 S9 z/ U) w% r
- fprintf(stderr, "Failed to initialize GLFW\n");! A) }: \/ j4 r9 H' E% f3 m: q
- return -1;9 W$ K! r0 l. [3 c- s1 M+ M7 T9 e1 M
- }
$ U& x6 Z* A9 t9 }3 S4 V - $ R& v0 V/ P: \. P2 {% x
- GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGL Text Rendering", NULL, NULL);
0 G- I8 O( d4 c' I3 }+ j1 A. Y - if (!window) {) P7 u& @- x2 Y8 i2 ?8 e
- fprintf(stderr, "Failed to create GLFW window\n");4 G+ L0 S$ ^6 y
- glfwTerminate();" D) X, T. K' v/ Q8 J' |1 }5 J, y
- return -1;
# ], p0 [! v: O; a) E8 p - }
z7 {5 O9 D0 ^
+ h2 z8 A( J: X/ |- glfwMakeContextCurrent(window); ~0 Q( y! \; \
- glewInit();: \. m" v, d4 n+ g ?
- ( D5 C% `3 R' }7 [
- initialize();. T+ t. F' f) a4 @5 U
; }5 W. Y: s, V/ }. e- while (!glfwWindowShouldClose(window)) {
8 R" M/ m/ N7 z9 s - glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
8 q. h% @( B3 y+ k7 e R- @ - glClear(GL_COLOR_BUFFER_BIT);
r: D6 w: Z/ J - - c7 j5 A; z: w0 r( e- R. A
- glColor3f(1.0f, 1.0f, 1.0f);
8 r/ u- K( m1 K( n2 I - renderString("Hello, OpenGL!", 100.0f, 100.0f);' s& ~: g- k" |+ g
- & {2 v( e$ R( W9 z/ H# `
- glfwSwapBuffers(window);" R; {4 V: Z1 y* p: ?
- glfwPollEvents();
; ^ T5 Q( A% X# n$ N+ \# U3 Y2 g* X - }
( X8 w; i) F/ ]1 z" f* T
* }3 O- n$ W! h: Z1 q- FT_Done_Face(face);
3 Y$ H7 b0 J% r; n# z0 c: j8 K& x' ~/ x - FT_Done_FreeType(library);: ^3 ~8 _0 T0 W0 M
- ; d( ~: W H, Q' j
- glfwTerminate();
& t! W3 r# i. o1 t( X1 y - " \$ v1 u) b8 w
- return 0;7 I# \. S B( M) i
- }
2 U/ Y$ [. V$ L; v' u( P7 G! S
复制代码
/ K9 r* ]( \* g
( q9 E7 r6 ^' j9 o |