使用Freetype和OpenGL渲染字符时出现问题

时间:2010-10-27 17:02:19

标签: c++ freetype

我正在尝试开发一个简单的2D游戏引擎,我需要能够绘制文本,所以我决定使用着名的freetype库。 我目前的目标是渲染0到255之间的每个字符,并将相应的信息存储在字形对象,字符的顶部和左侧位置,宽度和高度以及OpenGL纹理中。 (将来我希望删除每个字形有1个纹理,但在开始处理之前我需要正确显示我的字符)。 首先我把所有字符都弄乱但后来我检查了this question并想出我必须将位图缓冲区复制到一个新的OpenGL所需的纹理大小(即宽度和高度的2的幂)。从那以后,每个角色看起来都很棒,所以我尝试了不同的字体大小,看看是否一切正常,猜测是什么......它不是,小字体尺寸(即<30)每个宽度都小的字符('我','我',';',':')显示出乱码。

http://img17.imageshack.us/img17/2963/helloworld.png

所有扭曲的字符的宽度都是1,其中有两个,因为这是2的第一个幂,这是渲染每个字符的代码。

bool Font::Render(int PixelSize)
{
    if( FT_Set_Pixel_Sizes( mFace, 0, PixelSize ) != 0)
        return false;

    for(int i = 0; i < 256; ++i)
    {
        FT_UInt GlyphIndex;

        GlyphIndex = FT_Get_Char_Index( mFace, i );

        if( FT_Load_Glyph( mFace, GlyphIndex, FT_LOAD_RENDER ) != 0)
            return false;


        int BitmapWidth = mFace->glyph->bitmap.width;
        int BitmapHeight = mFace->glyph->bitmap.rows;

        int TextureWidth = NextP2(BitmapWidth);
        int TextureHeight= NextP2(BitmapHeight);

        printf("Glyph is %c, BW: %i, BH: %i, TW: %i, TH: %i\n", i, BitmapWidth, BitmapHeight, TextureWidth, TextureHeight);

        mGlyphs[i].SetAdvance(mFace->glyph->advance.x >> 6);
        mGlyphs[i].SetLeftTop(mFace->glyph->bitmap_left, mFace->glyph->bitmap_top);

        GLubyte * TextureBuffer = new GLubyte[ TextureWidth * TextureHeight ];

        for(int j = 0; j < TextureHeight; ++j)
        {
            for(int i = 0; i < TextureWidth; ++i)
            {
                TextureBuffer[ j*TextureWidth + i ] = (j >= BitmapHeight || i >= BitmapWidth ? 0 : mFace->glyph->bitmap.buffer[ j*BitmapWidth + i ]);
            }
        }

        for(int k = 0; k < TextureWidth * TextureHeight; ++k)
            printf("Buffer is %i\n", TextureBuffer[k]);

        Texture GlyphTexture;

        GLuint Handle;
        glGenTextures( 1, &Handle );

        GlyphTexture.SetHandle(Handle);
        GlyphTexture.SetWidth(TextureWidth);
        GlyphTexture.SetHeight(TextureHeight);

        glBindTexture(GL_TEXTURE_2D, Handle);

        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

        glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

        glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, TextureWidth, TextureHeight, 0, GL_ALPHA, GL_UNSIGNED_BYTE, TextureBuffer);

        mGlyphs[i].SetTexture(GlyphTexture);

        delete [] TextureBuffer;
    }

    return true;
}

我能找到的唯一解决方案是一个小if语句,如果它的值为2,则将TextureWidth更改为4,这样每个字体大小的文本看起来都很好。但这对我没有任何意义,为什么OpenGL会拒绝2个纹理的宽度?哦,我已经确定问题不在缓冲区内,我把它打印出来看起来很好。

你能想出发生这种情况的原因吗?

这是其余的(非常)凌乱的代码,以防问题不在Render函数中。

int NextP2(int Val)
{
    int RVal = 2;

 while(RVal < Val) RVal <<= 1;

 return RVal;
}

class Texture
{
    public:
        void Free() { glDeleteTextures(1, &mHandle); }

        void SetHandle(GLuint Handle) { mHandle = Handle; }
        void SetWidth(int Width) { mWidth = Width; }
        void SetHeight(int Height) { mHeight = Height; }

        inline GLuint Handle() const { return mHandle; }
        inline int Width() const { return mWidth; }
        inline int Height() const { return mHeight; }
    private:
        GLuint mHandle;
        int mWidth;
        int mHeight;
};

class Glyph
{
    public:

        void SetAdvance(int Advance) { mAdvance = Advance; }
        void SetLeftTop(int Left, int Top) { mLeft = Left; mTop = Top; }
        void SetTexture(Texture texture) { mTexture = texture; }

        Texture & GetTexture() { return mTexture; }
        int GetAdvance() const { return mAdvance; }
        int GetLeft() const { return mLeft; }
        int GetTop() const { return mTop; }

    private:
        int mAdvance;
        int mLeft;
        int mTop;
        Texture mTexture;

};

class Font
{
    public:
        ~Font() { for(int i = 0; i < 256; ++i) mGlyphs[i].GetTexture().Free(); }
        bool Load(const std::string & File);
        bool Render(int PixelSize);

        Glyph & GetGlyph(unsigned char CharCode) { return mGlyphs[CharCode]; }

    private:
        FT_Face mFace;
        Glyph mGlyphs[256];

};

我认为这就是一切,如果你发现问题,请告诉我。

最好的问候和提前谢谢,

4 个答案:

答案 0 :(得分:17)

我打赌我上周偶然发现了与freetype和OpenGL完全相同的问题。

OpenGL默认是考虑纹理数据行在4个字节边界上对齐。当纹理宽度变为2或1时,这会导致问题,这是有效的2次幂大小。

将行对齐设置为1个字节边界可以解决您的问题吗?使用

glPixelStore(GL_UNPACK_ALIGNMENT, 1)

HTH

答案 1 :(得分:0)

你可能遇到了纹理包装问题。

在OpenGls中,纹理的一个边框上渲染的像素“流血”到另一边,尝试平铺纹理。正确。 如果我是正确的,改变你将参数包装到

glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);

应该让opengl在边缘处夹住纹理,并正确地绘制小纹理。

<强>更新

我在tutorial page上发现了一些内容:

  

如果小字体的位图不是   与屏幕像素对齐,   双线性插值会导致   文物。一个简单的解决方法是   改变插值方法   尽管如此,GL_LINEAR到GL_NEAREST   可能会导致额外的文物   旋转文字的情况。 (如果旋转   你可能想要的是文字对你很重要   尝试告诉OpenGL超级样本   从较大的字体位图。)

答案 2 :(得分:0)

准备好支持&gt; 256个字符(这是历史) 以及从右到左和从左到右的文本流以及自上而下的流程。

对于说英语的人来说,只是一个不同的主题提示。

答案 3 :(得分:0)

将Freetype与ICU一起使用。单独的Freetype无法正确渲染字形。使用ICU。