2008-08-11 97 views
6

在Windows下,在opengl中显示unicode文本有没有好的方法?例如,当你必须处理不同的语言。最常见的方法如如何在OpenGL中显示unicode文本?

#define FONTLISTRANGE 128 
GLuint list; 
list = glGenLists(FONTLISTRANGE); 
wglUseFontBitmapsW(hDC, 0, FONTLISTRANGE, list); 

只是不会这样做,因为您无法为所有unicode字符创建足够的列表。

回答

0

您也可以按语言对字符进行分组。根据需要加载每个语言表,当需要切换语言时,卸载前一个语言表并加载新语言表。

+5

对于亚洲语言来说,这并不是一个很好的解决方案,其中原始字符数仍然非常巨大(数千字符),并且在任何给定的GL应用程序中,您可能只使用其中的一小部分。动态字形缓存方法更好。 – Baxissimo 2008-09-22 00:41:27

2

您可能需要在纹理内存中生成自己的“字形缓存”,可能会使用某种LRU策略来避免破坏所有纹理内存。不像您现在的方法那么容易,但是可能是给定unicode字符数的唯一方法

17

您还应该查看FTGL library

FTGL是++库,它使用对FreeType2 在OpenGL应用 简化渲染字体的免费的跨平台开放 源C。 FTGL支持位图, 像素图,纹理贴图,轮廓, 多边形网格和拉伸多边形 渲染模式。

这个项目已经休眠了一段时间,但最近还在开发中。我没有更新我的项目使用最新版本,但你应该检查出来。

它允许通过FreeType字体库使用任何TrueType字体。

+0

嗨,我使用的免费类型库,我真的很满意它。现在,当我将文本渲染为曲线时,性能变得非常慢,所以我使用字体来纹理技术来更快地显示。但是这样我就不能使用整个unicode字体,因为我只渲染了有限的字形。在这种情况下你有什么建议吗?我主要感兴趣的是支持英文和希腊文。 – 2017-03-10 17:24:37

6

我推荐读这个OpenGL font tutorial。它适用于D编程语言,但它很好地介绍了实现用OpenGL渲染文本的字形缓存系统所涉及的各种问题。本教程涵盖了Unicode合规性,抗锯齿和字距调整技术。

对于任何了解C++的人来说,D都是很容易理解的,大多数文章都是关于一般技术而不是实现语言。

+0

您的链接已死亡。 – 2017-03-10 17:23:41

1

Queso GLC非常适合这个,我用它来渲染3D中的中文和西里尔字符。

http://quesoglc.sourceforge.net/

它配备了应该让你开始的Unicode文本样本。

3

Id建议上面已经推荐的FTGL,但是我自己实现了一个freetype/OpenGL渲染器,并且认为如果您想自己重新创建这个轮子,您可能会发现代码方便。我真的推荐FTGL,但它使用起来很麻烦。 :)

* glTextRender class by Semi Essessi 
* 
* FreeType2 empowered text renderer 
* 
*/ 

#include "glTextRender.h" 
#include "jEngine.h" 

#include "glSystem.h" 

#include "jMath.h" 
#include "jProfiler.h" 
#include "log.h" 

#include <windows.h> 

FT_Library glTextRender::ftLib = 0; 

//TODO::maybe fix this so it use wchar_t for the filename 
glTextRender::glTextRender(jEngine* j, const char* fontName, int size = 12) 
{ 
#ifdef _DEBUG 
    jProfiler profiler = jProfiler(L"glTextRender::glTextRender"); 
#endif 
    char fontName2[1024]; 
    memset(fontName2,0,sizeof(char)*1024); 
    sprintf(fontName2,"fonts\\%s",fontName); 

    if(!ftLib) 
    { 
#ifdef _DEBUG 
     wchar_t fn[128]; 
     mbstowcs(fn,fontName,strlen(fontName)+1); 
     LogWriteLine(L"\x25CB\x25CB\x25CF Font: %s was requested before FreeType was initialised", fn); 
#endif 
     return; 
    } 

    // constructor code for glTextRender 
    e=j; 

    gl = j->gl; 

    red=green=blue=alpha=1.0f; 

    face = 0; 

    // remember that for some weird reason below font size 7 everything gets scrambled up 
    height = max(6,(int)floorf((float)size*((float)gl->getHeight())*0.001666667f)); 
    aHeight = ((float)height)/((float)gl->getHeight()); 

    setPosition(0.0f,0.0f); 

    // look in base fonts dir 
    if(FT_New_Face(ftLib, fontName2, 0, &face)) 
    { 
     // if we dont have it look in windows fonts dir 
     char buf[1024]; 
     GetWindowsDirectoryA(buf,1024); 
     strcat(buf, "\\fonts\\"); 
     strcat(buf, fontName); 

     if(FT_New_Face(ftLib, buf, 0, &face)) 
     { 
      //TODO::check in mod fonts directory 
#ifdef _DEBUG 
      wchar_t fn[128]; 
      mbstowcs(fn,fontName,strlen(fontName)+1); 
      LogWriteLine(L"\x25CB\x25CB\x25CF Request for font: %s has failed", fn); 
#endif 
      face = 0; 
      return; 
     } 
    } 

    // FreeType uses 64x size and 72dpi for default 
    // doubling size for ms 
    FT_Set_Char_Size(face, mulPow2(height,7), mulPow2(height,7), 96, 96); 

    // set up cache table and then generate the first 256 chars and the console prompt character 
    for(int i=0;i<65536;i++) 
    { 
     cached[i]=false; 
     width[i]=0.0f; 
    } 

    for(unsigned short i = 0; i < 256; i++) getChar((wchar_t)i); 
    getChar(CHAR_PROMPT); 

#ifdef _DEBUG 
    wchar_t fn[128]; 
    mbstowcs(fn,fontName,strlen(fontName)+1); 
    LogWriteLine(L"\x25CB\x25CB\x25CF Font: %s loaded OK", fn); 
#endif 
} 

glTextRender::~glTextRender() 
{ 
    // destructor code for glTextRender 
    for(int i=0;i<65536;i++) 
    { 
     if(cached[i]) 
     { 
      glDeleteLists(listID[i],1); 
      glDeleteTextures(1,&(texID[i])); 
     } 
    } 

    // TODO:: work out stupid freetype crashz0rs 
    try 
    { 
     static int foo = 0; 
     if(face && foo < 1) 
     { 
      foo++; 
      FT_Done_Face(face); 
      face = 0; 
     } 
    } 
    catch(...) 
    { 
     face = 0; 
    } 
} 


// return true if init works, or if already initialised 
bool glTextRender::initFreeType() 
{ 
    if(!ftLib) 
    { 
     if(!FT_Init_FreeType(&ftLib)) return true; 
     else return false; 
    } else return true; 
} 

void glTextRender::shutdownFreeType() 
{ 
    if(ftLib) 
    { 
     FT_Done_FreeType(ftLib); 
     ftLib = 0; 
    } 
} 

void glTextRender::print(const wchar_t* str) 
{ 
    // store old stuff to set start position 
    glPushAttrib(GL_TRANSFORM_BIT); 
    // get viewport size 
    GLint viewport[4]; 
    glGetIntegerv(GL_VIEWPORT, viewport); 

    glMatrixMode(GL_PROJECTION); 
    glPushMatrix(); 
    glLoadIdentity(); 

    gluOrtho2D(viewport[0],viewport[2],viewport[1],viewport[3]); 
    glPopAttrib(); 

    float color[4]; 
    glGetFloatv(GL_CURRENT_COLOR, color); 

    glPushAttrib(GL_LIST_BIT | GL_CURRENT_BIT | GL_ENABLE_BIT | GL_TRANSFORM_BIT); 
    glMatrixMode(GL_MODELVIEW); 
    glPushMatrix(); 
    glLoadIdentity(); 

    glEnable(GL_TEXTURE_2D); 
    //glDisable(GL_DEPTH_TEST); 

    // set blending for AA 
    glEnable(GL_BLEND); 
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 

    glTranslatef(xPos,yPos,0.0f); 

    glColor4f(red,green,blue,alpha); 

    // call display lists to render text 
    glListBase(0u); 
    for(unsigned int i=0;i<wcslen(str);i++) glCallList(getChar(str[i])); 

    // restore old states 
    glMatrixMode(GL_MODELVIEW); 
    glPopMatrix(); 
    glPopAttrib(); 

    glColor4fv(color); 

    glPushAttrib(GL_TRANSFORM_BIT); 
    glMatrixMode(GL_PROJECTION); 
    glPopMatrix(); 
    glPopAttrib(); 
} 

void glTextRender::printf(const wchar_t* str, ...) 
{ 
    if(!str) return; 

    wchar_t* buf = 0; 
    va_list parg; 
    va_start(parg, str); 

    // allocate buffer 
    int len = (_vscwprintf(str, parg)+1); 
    buf = new wchar_t[len]; 
    if(!buf) return; 
    vswprintf(buf, str, parg); 
    va_end(parg); 

    print(buf); 

    delete[] buf; 
} 

GLuint glTextRender::getChar(const wchar_t c) 
{ 
    int i = (int)c; 

    if(cached[i]) return listID[i]; 

    // load glyph and get bitmap 
    if(FT_Load_Glyph(face, FT_Get_Char_Index(face, i), FT_LOAD_DEFAULT)) return 0; 

    FT_Glyph glyph; 
    if(FT_Get_Glyph(face->glyph, &glyph)) return 0; 

    FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1); 

    FT_BitmapGlyph bitmapGlyph = (FT_BitmapGlyph)glyph; 
    FT_Bitmap& bitmap = bitmapGlyph->bitmap; 

    int w = roundPow2(bitmap.width); 
    int h = roundPow2(bitmap.rows); 

    // convert to texture in memory 
    GLubyte* texture = new GLubyte[2*w*h]; 

    for(int j=0;j<h;j++) 
    { 
     bool cond = j>=bitmap.rows; 

     for(int k=0;k<w;k++) 
     { 
       texture[2*(k+j*w)] = 0xFFu; 
       texture[2*(k+j*w)+1] = ((k>=bitmap.width)||cond) ? 0x0u : bitmap.buffer[k+bitmap.width*j]; 
     } 
    } 

    // store char width and adjust max height 
    // note .5f 
    float ih = 1.0f/((float)gl->getHeight()); 
    width[i] = ((float)divPow2(face->glyph->advance.x, 7))*ih; 
    aHeight = max(aHeight,(.5f*(float)bitmap.rows)*ih); 

    glPushAttrib(GL_LIST_BIT | GL_CURRENT_BIT | GL_ENABLE_BIT | GL_TRANSFORM_BIT); 

    // create gl texture 
    glGenTextures(1, &(texID[i])); 

    glEnable(GL_TEXTURE_2D); 

    glBindTexture(GL_TEXTURE_2D, texID[i]); 
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); 
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); 

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, texture); 

    glPopAttrib(); 

    delete[] texture; 

    // create display list 
    listID[i] = glGenLists(1); 

    glNewList(listID[i], GL_COMPILE); 

    glBindTexture(GL_TEXTURE_2D, texID[i]); 

    glMatrixMode(GL_MODELVIEW); 
    glPushMatrix(); 

    // adjust position to account for texture padding 
    glTranslatef(.5f*(float)bitmapGlyph->left, 0.0f, 0.0f); 
    glTranslatef(0.0f, .5f*(float)(bitmapGlyph->top-bitmap.rows), 0.0f); 

    // work out texcoords 
    float tx=((float)bitmap.width)/((float)w); 
    float ty=((float)bitmap.rows)/((float)h); 

    // render 
    // note .5f 
    glBegin(GL_QUADS); 
     glTexCoord2f(0.0f, 0.0f); 
     glVertex2f(0.0f, .5f*(float)bitmap.rows); 
     glTexCoord2f(0.0f, ty); 
     glVertex2f(0.0f, 0.0f); 
     glTexCoord2f(tx, ty); 
     glVertex2f(.5f*(float)bitmap.width, 0.0f); 
     glTexCoord2f(tx, 0.0f); 
     glVertex2f(.5f*(float)bitmap.width, .5f*(float)bitmap.rows); 
    glEnd(); 

    glPopMatrix(); 

    // move position for the next character 
    // note extra div 2 
    glTranslatef((float)divPow2(face->glyph->advance.x, 7), 0.0f, 0.0f); 

    glEndList(); 

    // char is succesfully cached for next time 
    cached[i] = true; 

    return listID[i]; 
} 

void glTextRender::setPosition(float x, float y) 
{ 
    float fac = ((float)gl->getHeight()); 
    xPos = fac*x+FONT_BORDER_PIXELS; yPos = fac*(1-y)-(float)height-FONT_BORDER_PIXELS; 
} 

float glTextRender::getAdjustedWidth(const wchar_t* str) 
{ 
    float w = 0.0f; 

    for(unsigned int i=0;i<wcslen(str);i++) 
    { 
     if(cached[str[i]]) w+=width[str[i]]; 
     else 
     { 
      getChar(str[i]); 
      w+=width[str[i]]; 
     } 
    } 

    return w; 
} 
2

您应该考虑使用一个统一渲染库(如:Pango)呈现的东西成位图,并将该位图在屏幕上或到纹理。

渲染unicode文本并不简单。所以你不能简单地加载64K矩形符号并使用它。

字符可能重叠。例如,在此笑脸:

(͡°͜ʖ͡°)

一些码点堆栈上的前一个字符的口音。考虑从这个notable post此摘录:

...他来了,喜Ş不圣洁的光芒destro҉ying所有 启蒙,HTML标记泄漏fr̶ǫm玩吧眼睛像 LIQ UID疼痛,定期exp的歌ression分析将 exti从这里可以看到它的sp男人的声音我可以看到它 你能看到͚̖͔̙î̩t͎̩͔̋它是美丽的最后吸鼻剂 男人的谎言全部都是我所有S丢失他来的胖人 他c̶̮omes他来了ich或渗透所有我的脸我的脸ᵒh上帝没有 NO NOOOØNΘ停止的*̶͑̾̾GLES 是N OT真正ZA̡͊͠͝LGΌISͮ҉̯͈͕̹̘TO͇̹̺Ɲ̴ȳ̳ 宝NYH̸̡̪̯ͨ͊̽̅̾Ȩ̬̩̾͛ͪ̈͘ ̶̧̨̹̭̯ͧ̾ͬC̷̙̝͖ͭ̏ͥͮ͟Oͮ͏̮̪̝͍M̖͊̒ͪͩͬ̚̚͜Ȇ̴̟̟͙̞ͩ͌͝S̨̥̫͎̭ͯ̿̔

如果你真的想渲染的Unicode正确,您应该能够也正确渲染这一个。

更新:看着这个Pango引擎,这是香蕉,大猩猩和整个丛林的情况。首先它依赖于Glib,因为它使用GObjects,其次它不能直接渲染到字节缓冲区。它具有Cario和FreeType后端,因此您必须使用其中一个来渲染文本并最终将其导出到位图中。目前看起来不太好。

除此之外,如果要将结果存储在纹理中,请在设置文本后使用pango_layout_get_pixel_extents以获取要呈现文本的矩形大小。墨水矩形是包含整个文本的矩形,它的左上角是相对于逻辑矩形左上角的位置。 (逻辑矩形的底线是基线)。希望这可以帮助。

相关问题