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

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

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

Как вы можете видеть, «O» и «T» правильные (если я изменю нижний левый и верхний правый углы текстуры, это будет абсолютно правильно). Но другие символы выглядят как смещенные (например, «E» смещается слева направо сверху вниз).

Полный код:

#include <math.h>
#include <iostream>
#include <GL/glew.h>
#include <GL/glut.h>

#include <ft2build.h>
#include FT_FREETYPE_HFT_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

Решение

Я немного разбирался в этом, и хотя этот ответ, возможно, неполон, возможно, он поможет вам разобраться.

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

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

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 обрабатывает верхний левый угол как его источник.

Вещи, которые могут помочь

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

Он копирует буфер растровых изображений в верхний левый угол другого буфера размером 64×64 байта, а затем создает из него мип-карты. Это результат:

результат

Дальнейшие заметки

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

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

3

Другие решения

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

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

0

По вопросам рекламы [email protected]