Неверная выходная текстура на квадроцикле

Я пытаюсь отобразить текст в своем приложении, используя 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;

и из дисплея():

 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();
}

результат кода

Как видите, "О" и "Т" правильные (если я изменю нижний левый и верхний правый углы текстуры, это будет абсолютно правильно). Но другие символы кажутся сдвинутыми (например, "Е" сдвинута слева сверху вниз).

Полный код:

#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;
}

person vlastachu    schedule 10.02.2013    source источник
comment
Странное поведение. Что произойдет, если вы поместите в свою строку только букву Q? Он так же искажен, как раньше? Кроме того, если вы поменяете местами буквы T и Q, будет ли T по-прежнему отображаться правильно, а Q искажено?   -  person Piotr Chojnacki    schedule 10.02.2013
comment
Почему вы передаете GL_RED gluBuild2DMipmaps() вместо GLU_RED? То же самое с GL_RGBA.   -  person genpfault    schedule 11.02.2013
comment
genpfault, потому что я не могу найти GLU_RED. Я никогда не слышал об этой константе. Москит, положение букв на рисунок не влияет.   -  person vlastachu    schedule 12.02.2013


Ответы (2)


Я немного изучал это, и хотя этот ответ, возможно, неполный, возможно, он поможет вам понять это.

Предварительное примечание

Прежде чем я перейду к тому, что я нашел, мне нужно указать на проблему с вашими текстурными координатами. У вас есть это:

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 не будет генерировать растровое изображение, размер которого обязательно равен степени двойки, что часто требуется для мип-мэппинга (см.: 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 байта, а затем строит из него MIP-карты. Вот результат:

результат

Дополнительные примечания

Мой код иллюстрации плохой, потому что он копирует данные растрового изображения для каждого глифа при каждой перерисовке и не принимает во внимание фактический размер буфера растрового изображения или если шаг больше 64. ) генерировать ваши мип-карты при каждой перерисовке, но если вы просто пытаетесь научиться вставлять слова в OpenGL, не беспокойтесь об этом :)

Изменить: мне пришлось использовать другой шрифт, потому что у меня нет вашего.

person tecu    schedule 10.02.2013
comment
Большое спасибо за отличный ответ. Проблема была не в размере глифа, но со степенью двойки он выглядит действительно лучше. А насчет угла букв - да это была моя глупая ошибка. - person vlastachu; 12.02.2013

Как сказал 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;

Будет лучше, если вы рассчитаете ширину и высоту (и округлите до степени двойки) перед подключением в массиве данных.

Если вам нужен кернинг, вы должны написать собственную более медленную реализацию memcpy, где вы будете добавлять (не полностью изменять) значение и проверять превышение UCHAR_MAX.

Мой окончательный результат: введите здесь описание изображения

person vlastachu    schedule 14.02.2013