渲染 text-freetype 空白屏幕
Posted
技术标签:
【中文标题】渲染 text-freetype 空白屏幕【英文标题】:Rendering text- freetype blank screen 【发布时间】:2013-08-13 15:28:06 【问题描述】:我正在使用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();
【问题讨论】:
首先,您将一个原始的、未初始化的指针传递给一个函数 (glGenTextures()
),该函数需要一个非空的 array 基指针。为您的纹理数组分配适当数量的内存或传递一个自动固定数组(在您的情况下,可能是后者,因为您只得到一个)。
我对那个函数的理解很糟糕——修改得当,现在我只是得到一个空白屏幕
【参考方案1】:
您的程序中有很多问题是由于不了解您对 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
结束评论。
此外,对于较新版本的 OpenGL,使用 gl_TexCoord
已被弃用,但如果您使用兼容性配置文件,它就可以工作。
顶点着色器
就像片段着色器一样,这里使用了已弃用的功能,即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
是向右的偏移量,而 y
是偏移到底部。所以如果你只是在 OpenGL 中使用这些坐标,一切都会颠倒过来。
如果你做了所有这些,你的结果应该看起来像这样(灰色背景突出多边形开始和结束的位置):
至于您的一般方法,重新利用一个纹理并逐个字母地重新使用和覆盖相同的纹理似乎是一种低效的方法。最好只分配一个更大的纹理,然后使用glTexSubImage2D
将字形写入它。如果 freetype 重新渲染字母是一个瓶颈,您也可以在开始时将所需的所有符号写入一个纹理(例如整个 ASCII 范围),然后将该纹理用作纹理图集。
我的一般建议也是,如果您不是真的想学习 OpenGL,而只是想使用一些跨平台渲染而不用担心低级的东西,我建议您改用渲染框架。
【讨论】:
感谢您的回复-我有几个问题,首先在 tex image 2d 调用中,从 glred 更改为 glluminance 的意义是什么?此外,当我的渲染时,我得到以下imgur.com/NptMgk6 我的代码可以在这里看到:pastebin.com/0M54fwpf 我打算让我的程序包含每个字符的映射,这些字符将在开头或第一次使用时添加 -这不是一个好方法吗? @user2673108 您需要将glBindTexture(GL_TEXTURE_2D, texture[0]);
放在GLuint texture[1];
之后,然后再更改与纹理相关的任何设置(所以直接放在后面)。如果您想保留当前方法,我建议您在方法末尾添加glBindTexture(GL_TEXTURE_2D, 0);
和glDeleteTextures(1,texture[0];);
。另外,你还没有做坐标变换,所以你的还是倒过来的。
@user2673108 和 GL_RED
仅将纹理上传到红色通道,因此文本会显示但它会显示为红色,而 GL_LUMINANCE
会将值复制到所有 3 个通道(红色、绿色,蓝色),因此使其显示为白色。
我已经按照你说的做了,而且效果很好,唯一的事情是我想删除字母周围的黑色填充,我尝试了以下片段着色器但它似乎没有工作pastebin.com/ESx0cLWw
@user2673108 OpenGL 是相当低级的,alpha
默认情况下什么都不做,如果你想把颜色当作你需要使用的 alpha 值在你绘制之前的某个时间使用glEnable(GL_BLEND);glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR);
(在glBegin()
) 之前。顺便提一句。您的着色器只会删除实际的黑色像素,它会在有提示的字形周围留下奇怪的伪影。另外,在发布到 *** 时,您应该尽量避免使用 pastebin 等网站,只需将代码添加到您的问题中,或者如果您需要添加大量代码,请提出一个新问题。以上是关于渲染 text-freetype 空白屏幕的主要内容,如果未能解决你的问题,请参考以下文章
渲染两个 RNCamera 组件时反应原生应用程序给出空白屏幕