我试图使用freetype在我的应用程序中显示文本。起初我认为这个内置函数(对于用于绘制文本的库来说是很自然的)。但是只有一个功能来显示符号。然后我决定将这些角色逐个带入纹理中。但是我又失望了:所有指南一个纹理使用单个图像(可能glTexSubImage2D可以帮助我吗?)。现在我在纹理和纹理上加上一个符号来表示opengl元素。这是我的代码(它'相当混乱,但现在我只想了解它是如何工作的):
//init:
if (FT_Init_FreeType(&ft)) {
fprintf(stderr, "Could not init freetype library\n");
return 0;
}
if (FT_New_Face(ft, fontfilename, 0, &face)) {
fprintf(stderr, "Could not open font %s\n", fontfilename);
return 0;
}
FT_Set_Pixel_Sizes(face, 0, 48); FT_GlyphSlot g = face->glyph;
并从display():
void display()
{
glClear(GL_COLOR_BUFFER_BIT);
glClearColor(1.0 ,1.0, 1.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();//load identity matrix
std::string s = "QWERTYOG0l ";
for(int i = 0; i < s.size(); i++){
FT_Load_Char( face, s[i], FT_LOAD_RENDER );
FT_GlyphSlot g = face->glyph;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR); // Linear Filtering
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_LINEAR); // Linear Filtering
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
gluBuild2DMipmaps( GL_TEXTURE_2D,
GL_RED,
g->bitmap.width,
g->bitmap.rows,
GL_RED,
GL_UNSIGNED_BYTE,
g->bitmap.buffer );
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f);glVertex3f(0.1f*i-0.1,0.07f,0.0f); //top left
glTexCoord2f(0.0f, 1.0f);glVertex3f(0.1f*i,0.07f,0.0f); //top right
glTexCoord2f(1.0f, 1.0f);glVertex3f(0.1f*i,-0.07f,0.0f); // bottom right
glTexCoord2f(1.0f, 0.0f);glVertex3f(0.1f*i-0.1,-0.07f,0.0f); //bottom left
glEnd();
}
正如你所看到的那样,&#34; O&#34;和&#34; T&#34;是正确的(如果我更改纹理的左下角和右上角,它将是绝对正确的)。但其他符号似乎已经移位(例如&#34; E&#34;从左到右从左移位)。
完整代码:
#include <math.h>
#include <iostream>
#include <GL/glew.h>
#include <GL/glut.h>
#include <ft2build.h>
#include FT_FREETYPE_H
FT_Library ft;
FT_Face face;
const char *fontfilename = "LucidaTypewriterBold.ttf";
GLuint texture[10];
GLint uniform_mytexture;
int setup() {
if (FT_Init_FreeType(&ft)) {
fprintf(stderr, "Could not init freetype library\n");
return 0;
}
if (FT_New_Face(ft, fontfilename, 0, &face)) {
fprintf(stderr, "Could not open font %s\n", fontfilename);
return 0;
}
FT_Set_Pixel_Sizes(face, 0, 48);
FT_Load_Char( face, 'O', FT_LOAD_RENDER );
FT_GlyphSlot g = face->glyph;
glGenTextures(1, &texture[0]); // Create The Texture
glBindTexture(GL_TEXTURE_2D, texture[0]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR); // Linear Filtering
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_LINEAR); // Linear Filtering
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, g->bitmap.width, g->bitmap.rows, GL_RED, GL_UNSIGNED_BYTE, g->bitmap.buffer);
return 1;
}
void display()
{
glClear(GL_COLOR_BUFFER_BIT);
glClearColor(1.0 ,1.0, 1.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();//load identity matrix
std::string s = "QWERTYOG0l ";
for(int i = 0; i < s.size(); i++){
FT_Load_Char( face, s[i], FT_LOAD_RENDER );
FT_GlyphSlot g = face->glyph;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR); // Linear Filtering
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_LINEAR); // Linear Filtering
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
gluBuild2DMipmaps( GL_TEXTURE_2D,
GL_RED,
g->bitmap.width,
g->bitmap.rows,
GL_RED,
GL_UNSIGNED_BYTE,
g->bitmap.buffer );
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f);glVertex3f(0.1f*i-0.1,0.07f,0.0f); //top left
glTexCoord2f(0.0f, 1.0f);glVertex3f(0.1f*i,0.07f,0.0f); //top right
glTexCoord2f(1.0f, 1.0f);glVertex3f(0.1f*i,-0.07f,0.0f); // bottom right
glTexCoord2f(1.0f, 0.0f);glVertex3f(0.1f*i-0.1,-0.07f,0.0f); //bottom left
glEnd();
}
//glActiveTexture(GL_TEXTURE0);
//glBindTexture(GL_TEXTURE_2D, texture[0]); // Select Our Texture
// glUniform1i(uniform_mytexture, /*GL_TEXTURE*/0);
glutPostRedisplay();
glutSwapBuffers();
}
void TimerFunction(int value)
{
}
int main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
glutInitWindowSize(800,600);
glutCreateWindow("Hello World");
//glutTimerFunc(30, TimerFunction, 1);
glewInit();
glEnable (GL_TEXTURE_2D);
setup();
glutDisplayFunc(display);
glutMainLoop();
return 0;
}
答案 0 :(得分:3)
我一直在研究这个问题,虽然这个答案可能不完整,但也许它可以帮助你解决这个问题。
在我找到我发现的内容之前,我需要指出纹理坐标的问题。你有这个:
glTexCoord2f(0.0f, 0.0f);glVertex3f(0.1f*i-0.1,0.07f,0.0f); //top left
glTexCoord2f(0.0f, 1.0f);glVertex3f(0.1f*i,0.07f,0.0f); //top right
glTexCoord2f(1.0f, 1.0f);glVertex3f(0.1f*i,-0.07f,0.0f); // bottom right
glTexCoord2f(1.0f, 0.0f);glVertex3f(0.1f*i-0.1,-0.07f,0.0f); //bottom left
它应该是这样的:
glTexCoord2f(0.0f, 0.0f);glVertex3f(0.1f*i-0.1,0.07f,0.0f); //top left
glTexCoord2f(1.0f, 0.0f);glVertex3f(0.1f*i,0.07f,0.0f); //top right
glTexCoord2f(1.0f, 1.0f);glVertex3f(0.1f*i,-0.07f,0.0f); // bottom right
glTexCoord2f(0.0f, 1.0f);glVertex3f(0.1f*i-0.1,-0.07f,0.0f); //bottom left
注意左上角对应于纹理坐标中的0,0,而1,1对应于右下角。这是因为(在这里猜测)freetype put将左上角视为其起源。
Freetype不会生成尺寸必须为2的幂的位图,这通常是mipmapping所需的(参见:https://gamedev.stackexchange.com/a/7929)。
因此,如果您要对此进行测试(注意:请勿在代码中实际使用此功能;这仅用于说明),您可以在gluBuild2DMipmaps
中替换display
来电使用以下内容(请务必#include <cstring>
:
int pitch = g->bitmap.pitch;
if (pitch < 0) {
pitch = -pitch;
}
unsigned char data[4096] = {0};
for (int row = 0; row < g->bitmap.rows; ++row) {
std::memcpy(data + 64 * row, g->bitmap.buffer + pitch * row, pitch);
}
gluBuild2DMipmaps(
GL_TEXTURE_2D,
GL_RGBA,
64,
64,
GL_RED,
GL_UNSIGNED_BYTE,
data
);
它的作用是将位图缓冲区复制到另一个64x64字节缓冲区的左上角,然后从中构建mipmap。这是结果:
我的插图代码很糟糕,因为它会在每次重绘时复制每个字形的位图数据,并且它不会考虑位图缓冲区的实际大小,或者如果音高大于64.您也可能不希望(重新)生成每次重绘的mipmap,但是如果你只是想学习如何将单词转换成OpenGL而不用担心它:)
编辑:我不得不使用与你不同的字体,因为我没有你的字体。
答案 1 :(得分:0)
正如tecu所说,正确的解决方案是使用两种尺寸的纹理。
在回答之前,我找到了另一个解决方案:
glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
之前的gluBuild2DMipmaps
。但在这里你会遇到更多问题,比如纹理周围的灰色边框
对于那些提出类似目标的人,我想分享一下我的经历:
在透明背景上制作黑色:
GLfloat swizzleMask[] = { 0,0,0, GL_RED};
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask);
UPD 使用OpenGL扩展有一个更简单明了的解决方案。
gluBuild2DMipmaps( GL_TEXTURE_2D,
GL_ALPHA,
g->bitmap.width,
g->bitmap.rows,
GL_RGBA,
GL_UNSIGNED_BYTE,
g->bitmap.buffer )
连接单个纹理中的所有字母
我认为这对于性能更好,但不确定我是否改变了正确的方式。
if(text[i] == ' ') left += 20; else
for (int row = 0; row < g->bitmap.rows; ++row) {
std::memcpy(data + left + 64*(strSize*(row + 64 - g->bitmap_top))
, g->bitmap.buffer + pitch * row, pitch);
}
left += g->advance.x >> 6;
如果在连接数据数组之前计算宽度和高度(并且舍入为2的幂)会更好。
如果你想要字距调整,你应该编写自己较慢的memcpy实现,在那里你将添加(不完全改变)值并检查超过UCHAR_MAX
。
我的最终结果: