四边形输出纹理不正确

时间:2013-02-10 17:47:06

标签: c++ opengl glut freetype

我试图使用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();
}

code result

正如你所看到的那样,&#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;
}

2 个答案:

答案 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。这是结果:

result

进一步说明

我的插图代码很糟糕,因为它会在每次重绘时复制每个字形的位图数据,并且它不会考虑位图缓冲区的实际大小,或者如果音高大于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

我的最终结果:enter image description here