我正在使用freetype,为了呈现文本,我唯一要做的就是将ft_bitmap转换为可以用opengl呈现的内容,有人可以解释如何执行此操作吗?我正在使用glfw。按照我试图这样做的方式它只是给出一个空白屏幕这是我正在使用的代码:
#include <exception>
#include <iostream>
#include <string>
#include <glew.h>
#include <GL/glfw.h>
#include <iterator>
#include "../include/TextRenderer.h"
#include <ft2build.h>
#include FT_FREETYPE_H
#include <stdexcept>
#include <freetype/ftglyph.h>
using std::runtime_error;
using std::cout;
TextRenderer::TextRenderer(int x, int y, FT_Face Face, std::string s)
{
FT_Set_Char_Size(
Face, /* handle to face object */
0, /* char_width in 1/64th of points */
16*64, /* char_height in 1/64th of points */
0, /* horizontal device resolution */
0 ); /* vertical device resolution */
slot= Face->glyph;
text = s;
setsx(x);
setsy(y);
penX = x;
penY = y;
face = Face;
//shaders
GLuint v = glCreateShader(GL_VERTEX_SHADER) ;
const char* vs = "void main(){ gl_Position = ftransform();}";
glShaderSource(v,1,&vs,NULL);
glCompileShader(v);
GLuint f = glCreateShader(GL_FRAGMENT_SHADER) ;
const char* fs = "uniform sampler2D texture1; void main() { gl_FragColor = texture2D(texture1, gl_TexCoord[0].st); //And that is all we need}";
glShaderSource(f,1,&fs,NULL);
glCompileShader(f);
Program= glCreateProgram();
glAttachShader(Program,v);
glAttachShader(Program,f);
glLinkProgram(Program);
}
void TextRenderer::render()
{
glUseProgram(Program);
FT_UInt glyph_index;
for ( int n = 0; n < text.size(); n++ )
{
/* retrieve glyph index from character code */
glyph_index = FT_Get_Char_Index( face, text[n] );
/* load glyph image into the slot (erase previous one) */
error = FT_Load_Glyph( face, glyph_index, FT_LOAD_RENDER );
draw(&face->glyph->bitmap,penX + slot->bitmap_left,penY - slot->bitmap_top );
penX += *(&face->glyph->bitmap.width)+3;
penY += slot->advance.y >> 6; /* not useful for now */
}
}
void TextRenderer::draw(FT_Bitmap * bitmap,float x,float y)
{
GLuint texture [0] ;
glGenTextures(1,texture);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
glTexImage2D (GL_TEXTURE_2D, 0, GL_RED , bitmap->width, bitmap->rows, 0, GL_RED , GL_UNSIGNED_BYTE, bitmap);
// int loc = glGetUniformLocation(Program, "texture1");
// glUniform1i(loc, 0);
glBindTexture(GL_TEXTURE_2D, texture[0]);
glEnable(GL_TEXTURE_2D);
int height=bitmap->rows/10;
int width=bitmap->width/10;
glBegin(GL_QUADS);
glTexCoord2f (0.0, 0.0);
glVertex2f(x,y);
glTexCoord2f (1.0, 0.0);
glVertex2f(x+width,y);
glTexCoord2f (1.0, 1.0);
glVertex2f(x+width,y+height);
glTexCoord2f (0.0, 1.0);
glVertex2f(x,y+height);
glEnd();
glDisable(GL_TEXTURE_2D);
}
我用来初始化文本渲染器:
FT_Library library;
FT_Face arial;
FT_Error error = FT_Init_FreeType( &library );
if ( error )
{
throw std::runtime_error("Freetype failed");
}
error = FT_New_Face( library,
"C:/Windows/Fonts/Arial.ttf",
0,
&arial );
if ( error == FT_Err_Unknown_File_Format )
{
throw std::runtime_error("font format not available");
}
else if ( error )
{
throw std::runtime_error("Freetype font failed");
}
TextRenderer t(5,10,arial,"Hello");
t.render();
答案 0 :(得分:2)
您的程序中存在很多问题,这些问题是由于不了解您对OpenGL或Freetype的每个调用所做的。您应该真正阅读库的文档,而不是相互堆叠教程。
让我们逐一做到这一点
片段着色器
const char* fs = "uniform sampler2D texture1;
void main() {
gl_FragColor = texture2D(texture1, gl_TexCoord[0].st);
//And that is all we need}";`
此着色器无法编译(您应该检查它是否与glGetShaderiv
进行编译,以及是否与glGetProgramiv
链接)。如果你正确地缩进它,那么你会看到你注释掉了最后的}
,因为它在同一行和//
之后。因此,您应该删除评论或使用\n
结束评论。
此外,对于使用gl_TexCoord
的较新版本的OpenGL,不建议使用,但如果您使用兼容性配置文件,则可以使用。
顶点着色器
就像片段着色器一样,使用了不推荐使用的功能,即ftransform()
。
但更大的问题是你在片段着色器中使用gl_TexCoord[0]
而不是从顶点着色器传递它。因此,您需要在顶点着色器中添加行gl_TexCoord[0]=gl_MultiTexCoord0;
。 (正如您可能已经猜到的那样也被弃用了)
纹理传递
您正在将指针bitmap
传递给glTexImage2D
,但bitmap
的类型为FT_Bitmap *
,您需要传递bitmap->buffer
。
你不应该为每一帧的每个字母生成一个新的纹理(特别是如果你没有删除它)。你应该只调用glGentextures
一次(你可以把它放在TextRenderer
构造函数中,因为你把所有其他初始化的东西放在那里)。
然后是GLuint texture [0];
,它会给你一个编译器错误。如果你真的需要一个包含一个元素的数组,那么语法是GLuint texture [1];
所以你最后的电话会是这样的:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, bitmap->width, bitmap->rows, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, bitmap->buffer);
<强>杂强>
int height=bitmap->rows/10;
int width=bitmap->width/10;
这是一个整数除法,如果bitmap->width
的值小于10
,则会得到0
作为结果,这会使您尝试绘制的四边形不可见(高度或宽度为0)。如果您无法将对象放入视图中,则应将其翻译/缩放到视图中。这也被弃用,但如果你继续使用其他东西,这将使你的窗口有一个从[-100,-100]到[100,100](左下角到右上角)的坐标系。
glLoadIdentity();
glScalef(0.01f, 0.01f, 1.0f);
你也错过了从FreeType到OpenGL的坐标转换,Freetype使用了一个坐标系,它从左上角的[0,0]开始,x
是右边的偏移{{1是底部的偏移量。因此,如果你只是在OpenGL中使用这些坐标,那么一切都会颠倒过来。
如果你做了所有这些,你的结果应该是这样的(灰色背景突出显示多边形的开始和结束位置):
至于你的一般方法,重新利用一个纹理和逐字母绘制重新使用和覆盖相同的纹理似乎是一种低效的方法。最好只分配一个较大的纹理,然后使用y
将字形写入其中。如果freetype重新渲染字母是一个瓶颈,你也可以在开始时将所需的所有符号写入一个纹理(例如整个ASCII范围),然后将该纹理用作纹理图集。
我的一般建议也是如果你真的不想学习OpenGL而只是想使用一些跨平台渲染而不打扰低级别的东西,我建议使用渲染框架。