windows 如何在 OpenGL 中显示 unicode 文本?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7737/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 11:05:31  来源:igfitidea点击:

How to display unicode text in OpenGL?

windowsopenglunicode

提问by Serge

Is there a good way for displaying unicode text in opengl under Windows? For example, when you have to deal with different languages. The most common approach like

有没有什么好办法在windows下用opengl显示unicode文本?例如,当您必须处理不同的语言时。最常见的方法如

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

just won't do because you can't create enough lists for all unicode characters.

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

采纳答案by Andreas Müller

You could also group the characters by language. Load each language table as needed, and when you need to switch languages, unload the previous language table and load the new one.

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

回答by Rob Thomas

You should also check out the FTGL library.

您还应该查看FTGL 库

FTGL is a free cross-platform Open Source C++ library that uses Freetype2 to simplify rendering fonts in OpenGL applications. FTGL supports bitmaps, pixmaps, texture maps, outlines, polygon mesh, and extruded polygon rendering modes.

FTGL 是一个免费的跨平台开源 C++ 库,它使用 Freetype2 来简化 OpenGL 应用程序中的渲染字体。FTGL 支持位图、像素图、纹理图、轮廓、多边形网格和挤压多边形渲染模式。

This project was dormant for awhile, but is recently back under development. I haven't updated my project to use the latest version, but you should check it out.

这个项目休眠了一段时间,但最近又重新开始开发。我还没有更新我的项目以使用最新版本,但你应该检查一下。

It allows for using any True Type Font via the FreeTypefont library.

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

回答by Baxissimo

I recommend reading this OpenGL font tutorial. It's for the D programming language but it's a nice introduction to various issues involved in implementing a glyph caching system for rendering text with OpenGL. The tutorial covers Unicode compliance, antialiasing, and kerning techniques.

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

D is pretty comprehensible to anyone who knows C++ and most of the article is about the general techniques, not the implementation language.

D 对于任何了解 C++ 的人来说都很容易理解,而且大部分文章都是关于通用技术,而不是实现语言。

回答by jheriko

Id recommend FTGL as already recommended above, however I have implemented a freetype/OpenGL renderer myself and thought you might find the code handy if you want reinvent this wheel yourself. I'd really recommend FTGL though, its a lot less hassle to use. :)

我推荐上面已经推荐的 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;
}

回答by Calmarius

You should consider using an Unicode rendering library (eg. Pango) to render the stuff into a bitmap and put that bitmap on the screen or into a texture.

您应该考虑使用 Unicode 渲染库(例如Pango)将内容渲染为位图并将该位图放在屏幕上或放入纹理中。

Rendering unicode text is not simple. So you cannot simply load 64K rectangular glyphs and use it.

呈现 unicode 文本并不简单。所以你不能简单地加载 64K 矩形字形并使用它。

Characters may overlap. Eg in this smiley:

字符可能会重叠。例如在这个笑脸中:

( ?° ?? ?°)

( ?° ?? ?°)

Some code points stack accents on the previous character. Consider this excerpt from this notable post:

一些代码点在前一个字符上叠加重音。考虑一下这篇著名帖子的摘录:

...he com?e?s, ?h?i?s un?ho?ly radian?ce? destro?ying all enli????ghtenment, HTML tags lea?ki?n?g fr?o?m ?yo??ur eye?s? ?l?ik?e liq?uid pain, the song of re?gular exp?ression parsing will exti?nguish the voices of mor?tal man from the sp?here I can see it can you see ?????i???t???????? it is beautiful t?he final snuffing of the lie?s of Man ALL IS LOS????????T ALL I?S LOST the pon?y he comes he c??omes he comes the ich?or permeates all MY FACE MY FACE ?h god no NO NOO?O?O NΘ stop the an?*??????????g????????l??????????e??s ?a???r?????e n?ot re????a?l???????? ZA????LG? IS????????? TO???????? TH?E??? ?P???O??N?Y? H??????????E?????????? ??????????C??????????O??????M??????????E?????????S??????????

...他来了?e?s,?h?i?s un?ho?ly radian?ce?摧毁?所有enli????ghtenment,HTML 标签lea?ki?n?g fr?o?m ?yo??ur eye?s? ?l?ik?e 液体疼痛,正则表达式解析之歌将熄灭凡人的声音从 sp?here I can see it can you see ?????i ????????????? 它是美丽的?他最后扼杀了谎言?男人的一切都失去了??????????T ALL I?S LOST the pon?y he come he c??omes he come the ich?or peresates所有我的脸我的脸?e??s ?a???r???en?ot re????a?l?????????? ZA????LG? 是?????????到????????这????小马?他????????????????????C??????????O??????M??????????E?????????S ?????????

If you truly want to render Unicode correctly you should be able to render this one correctly too.

如果你真的想正确地呈现 Unicode,你也应该能够正确地呈现这个。

UPDATE: Looked at this Pango engine, and it's the case of banana, the gorilla, and the entire jungle. First it depends on the Glib because it used GObjects, second it cannot render directly into a byte buffer. It has Cario and FreeType backends, so you must use one of them to render the text and export it into bitmaps eventually. That's doesn't look good so far.

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

In addition to that, if you want to store the result in a texture, use pango_layout_get_pixel_extentsafter setting the text to get the sizes of rectangles to render the text to. Ink rectangle is the rectangle to contain the entire text, it's left-top position is the position relative to the left-top of the logical rectangle. (The bottom line of the logical rectangle is the baseline). Hope this helps.

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

回答by basszero

You may have to generate you own "glyph cache" in texture memory as you go, potentially with some sort of LRU policy to avoid destroying all of the texture memory. Not nearly as easy as your current method, but may be the only way given the number of unicode chars

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

回答by Andreas Müller

Queso GLC is great for this, I've used it to render Chinese and Cyrillic characters in 3D.

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

http://quesoglc.sourceforge.net/

http://quesoglc.sourceforge.net/

The Unicode text sample it comes with should get you started.

它附带的 Unicode 文本示例应该可以帮助您入门。

回答by Richard Kirk

Unicode is supported in the title bar. I have just tried this on a Mac, and it ought to work elsewhere too. If you have (say) some imported data including text labels, and some of the labels just might contain unicode, you could add a tool that echoes the label in the title bar.

标题栏中支持 Unicode。我刚刚在 Mac 上试过这个,它也应该在其他地方工作。如果您有(比如说)一些导入的数据,包括文本标签,而一些标签可能只包含 unicode,您可以添加一个工具来与标题栏中的标签相呼应。

It's not a great solution, but it is very easy to do.

这不是一个很好的解决方案,但很容易做到。