我正在尝试开发一个简单的2D游戏引擎,我需要能够绘制文本,所以我决定使用着名的freetype库.我目前的目标是渲染0到255之间的每个字符,并将相应的信息存储在字形对象,字符的顶部和左侧位置,宽度和高度以及OpenGL纹理中.(将来我希望删除每个字形有1个纹理,但在开始处理之前我需要正确显示我的字符).首先,我将所有字符都弄乱,但后来我查看了这个问题并想通过我必须将位图缓冲区复制到一个具有OpenGL所需纹理大小的新缓冲区(即宽度和高度的2的幂).从那以后,每个角色看起来都很棒,所以我尝试了不同的字体大小,看看是否一切正常,猜测是什么......它不是,小字体(即<30)每个宽度较小的字符( 'l','我',';',':')出现乱码.
http://img17.imageshack.us/img17/2963/helloworld.png
所有扭曲的字符的宽度都是1,它是2,因为这是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;
}
Run Code Online (Sandbox Code Playgroud)
我能找到的唯一解决方案是一个小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];
};
Run Code Online (Sandbox Code Playgroud)
我认为这是关于一切的,如果你发现问题,请告诉我.
最诚挚的问候和提前谢谢,
rot*_*lup 17
我打赌我上周偶然发现了与freetype和OpenGL完全相同的问题.
OpenGL默认是考虑纹理数据行在4个字节边界上对齐.当纹理宽度变为2或1(这是2的有效功率)时,这会导致问题.
将行对齐设置为1字节边界是否可以解决您的问题?运用
glPixelStore(GL_UNPACK_ALIGNMENT, 1)
Run Code Online (Sandbox Code Playgroud)
HTH
您可能遇到了纹理包裹问题。
在 OpenGL 中,纹理一个边框上渲染的像素会“渗透”到另一侧,尝试平铺纹理。正确。如果我是正确的,请将您的包装参数更改为
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
Run Code Online (Sandbox Code Playgroud)
应该让 opengl 在其边缘夹紧你的纹理,并正确绘制你的小纹理。
更新
我在教程页面上找到了一些东西:
如果小字体的位图未与屏幕像素对齐,双线性插值将导致伪影。解决此问题的一个简单方法是将插值方法从 GL_LINEAR 更改为 GL_NEAREST,尽管这可能会在旋转文本的情况下导致额外的伪像。(如果旋转文本对您来说很重要,您可能需要尝试告诉 OpenGL 从较大的字体位图中进行超级采样。)