Помогите пожалуйста загрузить шрифт и написать какую-нибудь надпись в окне...
Смотрел примеры freetype, oglft. Честно сказать немного внимание рассеивалось... Ну а на многих сайтах используют старые фичи OGL(а я хотел бы сразу делать под OpenGL 3.3+) + используют GLUT(а я его не использую).
Если же примером не поделитесь... То скажите пожалуйста: что хранится в ttf шрифтах? Хочу попробовать самостоятельно загрузить шрифт.
И как, имея загруженный шрифт, дальше с ним работать для рендера текста?
Знаю, вопрос ну ооочень глупый... Многие из вас скорее всего facepalm ставят из-за моего очередного вопроса... Но я правда целый день пытался поработать с текстом, искал, пробовал... Ну не получается у меня( Я знаю как такое сделать с помощью SDL или SFML, но мне хотелось бы более детально в этом всём разобраться...
В ttf шрифтах хранится геометрическое представление символов в векторном виде, т.е. в виде отрезков/сплайнов и пр., так что нарисовать их сам ты врядли сможешь. Используй freetype, чтобы он тебе нарисовал нужные символы в нужном размере в текстуру, потом просто накладывай эту текстуру.
Более простым наверно способом будет сразу создать текстуру с нужными символами, может даже какой-нибудь сторонней программой. Недостатком такого подхода является то, что во первых нельзя будет программно менять размеры символов, а так же, что набор символов будет фиксирован (т.е. например латинские + русские буквы и пр.), если у тебя текст хранится в Unicode, и вдруг потребуется написать что-нибудь там на немецком/китайском/японском, то ничего не получится (скажем например это онлайн игра с общим чатом, и какой-то японец решил отписаться). С ttf правда тоже не факт, что он будет содержать символы этих языков, но там все равно свободы больше.
ud1
> freetype, чтобы он тебе нарисовал нужные символы в нужном размере в текстуру,
> потом просто накладывай эту текстуру.
Я бы с радостью, но не смог разобраться. Вполне может быть, что я читал плохой пример. Что-нибудь посоветуете в качестве примера?
Примеров не знаю. Как так не смог разобраться? Разберись. Читай доки, вникай.
Для 2D текста мне кажется лучше freetype не найти.
Я тоже долго вкуривал эту тему, но результат того стоил. Идея следующая
( из .Net обвязки)
1. Загружаем шрифт
[DllImport(FT_NATIVE_LIBRARY, CallingConvention = CALLING_CONVENTION), SuppressUnmanagedCodeSecurity]
public static extern int FT_New_Memory_Face(IntPtr /*LibraryRec_*/ library, [In] byte[] file_base, int file_size, int face_index, out IntPtr /*IntPtr FaceRec*/ aface);
2. Установка размера
[DllImport(FT_NATIVE_LIBRARY, CallingConvention = CALLING_CONVENTION), SuppressUnmanagedCodeSecurity]
public static extern int FT_Set_Char_Size(IntPtr /*FaceRec*/ face, int char_width, int char_height, uint horz_resolution, uint vert_resolution);
3. Установка размера пикселя
[DllImport(FT_NATIVE_LIBRARY, CallingConvention = CALLING_CONVENTION), SuppressUnmanagedCodeSecurity]
public static extern int FT_Set_Pixel_Sizes(IntPtr /*FaceRec*/ face, uint pixel_width, uint pixel_height);
4. Загрузка символов в текстуры
list_base = Gl.glGenLists(list_base_size);
Gl.glGenTextures(list_base_size, textures);
for (int c = 0; c < list_base_size; c++)
{
Compile_Character(face, faceptr, (byte)c);
}
public void Compile_Character(FT_FaceRec face, System.IntPtr faceptr, int c)
{
//We first convert the number index to a character index
uint index = FT.FT_Get_Char_Index(faceptr, c);
//Here we load the actual glyph for the character
int ret = FT.FT_Load_Glyph(faceptr, index, (int)FT_LOAD_TYPES.FT_LOAD_DEFAULT);
if (ret != 0) return;
//Convert the glyph to a bitmap
System.IntPtr glyph;
int retb = FT.FT_Get_Glyph(face.glyph, out glyph);
if (retb != 0) return;
//GlyphRec glyph_rec=(GlyphRec)Marshal.PtrToStructure( face.glyphrec, typeof(GlyphRec) );
FT.FT_Glyph_To_Bitmap(out glyph, FT_RENDER_MODES.FT_RENDER_MODE_LIGHT, 0, 1);
BitmapGlyph glyph_bmp = (BitmapGlyph)Marshal.PtrToStructure(glyph, typeof(BitmapGlyph));
int size = (glyph_bmp.bitmap.width * glyph_bmp.bitmap.rows);
symbolWidth[c] = glyph_bmp.bitmap.width;
if (size <= 0)
{
//space is a special `blank` character
extent_x[c] = 0;
if (c == 32)
{
Gl.glNewList((uint)(list_base + c), Gl.GL_COMPILE);
Gl.glTranslatef(font_size >> 1, 0, 0);
extent_x[c] = font_size >> 1;
symbolWidth[c] = font_size >> 1;
Gl.glEndList();
}
return;
}
byte[] bmp = new byte[size];
Marshal.Copy(glyph_bmp.bitmap.buffer, bmp, 0, bmp.Length);
//Next we expand the bitmap into an opengl texture
int width = next_po2(glyph_bmp.bitmap.width);
int height = next_po2(glyph_bmp.bitmap.rows);
byte[] expanded = new byte[2 * width * height];
for (int j = 0; j < height; j++)
{
for (int i = 0; i < width; i++)
{
expanded[2 * (i + j * width)] = expanded[2 * (i + j * width) + 1] =
(i >= glyph_bmp.bitmap.width || j >= glyph_bmp.bitmap.rows) ?
(byte)0 : bmp[i + glyph_bmp.bitmap.width * j];
}
}
//Set up some texture parameters for opengl
Gl.glBindTexture(Gl.GL_TEXTURE_2D, textures[c]);
Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, Gl.GL_LINEAR);
Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_LINEAR);
//Create the texture
Gl.glTexImage2D(Gl.GL_TEXTURE_2D, 0, Gl.GL_RGBA, width, height,
0, Gl.GL_LUMINANCE_ALPHA, Gl.GL_UNSIGNED_BYTE, expanded);
expanded = null;
bmp = null;
//Create a display list and bind a texture to it
Gl.glNewList((uint)(list_base + c), Gl.GL_COMPILE);
Gl.glBindTexture(Gl.GL_TEXTURE_2D, textures[c]);
//Account for freetype spacing rules
Gl.glTranslatef(glyph_bmp.left + ((c>=48 && c<58) ? 1 : 0), 0, 0);
Gl.glPushMatrix();
Gl.glTranslatef(0, glyph_bmp.top - glyph_bmp.bitmap.rows, 0);
float x = (float)glyph_bmp.bitmap.width / (float)width;
float y = (float)glyph_bmp.bitmap.rows / (float)height;
//Draw the quad
Gl.glBegin(Gl.GL_QUADS);
Gl.glTexCoord2d(0, 0); Gl.glVertex2f(0, glyph_bmp.bitmap.rows);
Gl.glTexCoord2d(0, y); Gl.glVertex2f(0, 0);
Gl.glTexCoord2d(x, y); Gl.glVertex2f(glyph_bmp.bitmap.width, 0);
Gl.glTexCoord2d(x, 0); Gl.glVertex2f(glyph_bmp.bitmap.width, glyph_bmp.bitmap.rows);
Gl.glEnd();
Gl.glPopMatrix();
//Advance for the next character
Gl.glTranslatef(glyph_bmp.bitmap.width, 0, 0);
extent_x[c] = glyph_bmp.left + glyph_bmp.bitmap.width;
Gl.glEndList();
}
5. Печать текста
public void print(float x, float y, string what)
{
int font = list_base;
//Prepare openGL for rendering the font characters
push_scm();
Gl.glPushAttrib(Gl.GL_LIST_BIT | Gl.GL_CURRENT_BIT | Gl.GL_ENABLE_BIT | Gl.GL_TRANSFORM_BIT);
Gl.glMatrixMode(Gl.GL_MODELVIEW);
Gl.glDisable(Gl.GL_LIGHTING);
Gl.glEnable(Gl.GL_TEXTURE_2D);
Gl.glDisable(Gl.GL_DEPTH_TEST);
Gl.glEnable(Gl.GL_BLEND);
Gl.glBlendFunc(Gl.GL_SRC_ALPHA, Gl.GL_ONE_MINUS_SRC_ALPHA);
Gl.glListBase(font);
float[] modelview_matrix = new float[16];
Gl.glGetFloatv(Gl.GL_MODELVIEW_MATRIX, modelview_matrix);
Gl.glPushMatrix();
Gl.glLoadIdentity();
Gl.glTranslatef(x, y, 0);
Gl.glMultMatrixf(modelview_matrix);
//Render
//byte[] textbytes = new byte[what.Length];
//for (int i = 0; i < what.Length; i++)
// textbytes = (byte)what;
Gl.glCallLists(what.Length, Gl.GL_UNSIGNED_SHORT, what);
//textbytes = null;
//Restore openGL state
Gl.glPopMatrix();
Gl.glPopAttrib();
pop_pm();
}
Yanusa3
Написано же, на OpenGL 3.3 (никаких glNewList и glBegin)
Laynos
в общих чертах:
загружаешь с помошью freetype шрифт -> делаешь в зависимости от языка атлас из необходимых символов (в тестуру), сохраняя параметры символа (юникод ID, текстурные координаты)
Для нужного текста собираешь VBO -> Выводишь.
MtriG
> в общих чертах:
> загружаешь с помошью freetype шрифт -> делаешь в зависимости от языка атлас из
> необходимых символов (в тестуру), сохраняя параметры символа (юникод ID,
> текстурные координаты)
> Для нужного текста собираешь VBO -> Выводишь.
Ну да, как-то так я себе это и представлял. Но... Как определить, что в таком-то месте стоит, скажем, буква "а"?
Я планирую загрузить шрифт в некоторый атлас, потом каждую букву\цифру\символ заносить в std::map. Т.е. при подачи текста для рендера функция будет разбирать слова на символы, искать их по map, после чего добавлять в VBO. Когда всё готово, VBO отдаётся видеокарте. По крайней мере до этого я вчера додумался. Так это или нет - сложно мне судить... Да и из-за map, как по мне, производительность хорошенько просядет. Слышал что-то про кешированный map, но я пока не понимаю как он работает.
Yanusa3
Жалко, что для старой версии OGL...( Но всё равно спасибо
Кстати, а glEnable остаётся частью OGL 3.3?
Laynos
Да все правильно. Используй std::unordered_map - по шустрее (он использует хеш таблицы). Работать с ним так же, как и с std::map.
Можно использовать просто массив и при этом еще хранить int смещения используемых символов в юникод кодировке (для латинской = 0 , для кириллицы = 1024(вроде)..)
Laynos
Да конечно
Laynos,
Да, конечно, многие фичи без glEnable/glDisable не сделать. GL_DEPTH_TEST, GL_CULL_FACE, к примеру, как были, так и остались =)
Вместо map, конечно, можно попробовать использовать массив, устанавливающий параметры для каждого юникодовского символа, но это как-то чересчур, если ты не хочешь сразу все языки поддерживать. Мне тоже кажется, что map вполне подойдет. Однако было бы интересно ознакомиться с мнением более опытных товарищей :)
https://code.google.com/p/freetype-gl/
Пример с VBO
http://www.mbsoftworks.sk/index.php?page=tutorials&series=1&tutorial=12
Пример с VBO
Вся польза в скорости использования VBO улетает в трубу, т.к. в этом примере для каждого символа вызывается :
// Draw letter
glDrawArrays(GL_TRIANGLE_STRIP, iIndex*4, 4);
Лучше собирать все необходимое в индексный массив и рисовать одним вызовом.
MtriG
> Пример с VBO
> Вся польза в скорости использования VBO улетает в трубу, т.к. в этом примере
> для каждого символа вызывается :
>
> // Draw letter
> glDrawArrays(GL_TRIANGLE_STRIP, iIndex*4, 4);
>
> Лучше собирать все необходимое в индексный массив и рисовать одним вызовом.
То есть всю нужную инфу класть, скажем, в std::vector, после чего отправлять информацию в видеокарту и вызывать соответствующий шейдер?
MtriG
> Да все правильно. Используй std::unordered_map - по шустрее (он использует хеш
> таблицы). Работать с ним так же, как и с std::map.
> Можно использовать просто массив и при этом еще хранить int смещения
> используемых символов в юникод кодировке (для латинской = 0 , для кириллицы =
> 1024(вроде)..)
Про std::unordered_map сейчас поищу в своём справочнике... Таки интересно почему он пошустрее :)
Эм... Ещё раз извиняюсь за дурной вопрос, но что это за смещение? Смещение относительно чего? (относительно начала массива?)
Хранить адрес каждого символа?
allcreater
Ок, понял :)
Yanusa3
> https://code.google.com/p/freetype-gl/
>
> Пример с VBO
> http://www.mbsoftworks.sk/index.php?page=tutorials&series=1&tutorial=12
Спасибо, ознакомлюсь с кодом!
Не подскажите почему FT_Get_Char_Index может возвращать 0 для русских букв? (кстати сдвиг для русских букв 1040)
Тема в архиве.