iDevGames Forums
2D Font Rendering - Printable Version

+- iDevGames Forums (http://www.idevgames.com/forums)
+-- Forum: Development Zone (/forum-3.html)
+--- Forum: iPhone, iPad & iPod Game Development (/forum-11.html)
+--- Thread: 2D Font Rendering (/thread-8826.html)



2D Font Rendering - SparkyNZ - Apr 12, 2011 03:32 AM

I have a .TGA file (here) with some font characters in it. Each character is 8 pixels wide and 9 pixels deep (bottom line is always blank). I used a .TGA file so I could specify transparency.

I've been having problems rendering fonts with OpenGL for 2-3 weeks. I thought I was getting there but my recent move to the iPhone has caused me grief again. I've obviously resorted to hacking it to work rather than understanding how to do it properly.

Q1) I'm seeing anti-aliasing taking place. What I want to do is render my fonts pixel perfect. How do I do this?

Q2) I keep getting "bleeding" taking place which would suggest to me that my source coordinates are wrong and as a result I end up copying bits of surrounding characters when I try to render one character.

I'm keeping g_fCharPixelHeight and g_fCharPixelWidth as multiples of 8 so I should in theory have perfect scaling for the characters when the source characters are actually 8x8.

Code:
void sprChar( int x, int y, char c, BOOL fCharCoords )
{
  int i = c;

  glBindTexture( GL_TEXTURE_2D, Texture[ 0 ]);
  
  glPushMatrix();
  glDisable( GL_BLEND );  
  
  // PDS: Stupid 16x16 characters are not really that big but are placed in the middle..
  float fSourceHeight     = 8.0f;
  float fSourceRowSpacing = 1.0f;
  float fCharsPerRow = 16.0f;
  float fCharsPerCol = 128.0f / ( fSourceRowSpacing + fSourceHeight );
  float fTexXStart = 0.0f;
  float fTexYStart = 0.0f;
  float fTexWidth  = 1.0f / fCharsPerRow;
  float fTexHeight = 1.0f / fCharsPerCol;
  
  float fOnePixelHeight = fTexHeight / g_fCharPixelHeight;
  float fOnePixelWidth  = fTexWidth  / g_fCharPixelWidth;
  
  float tX = g_fCharPixelWidth  / 2.0;
  float tY = g_fCharPixelHeight / 2.0;
  
  float xOffset = 0;
  float yOffset = 0;
  
  // PDS: Offset the drawing by half character width since all placement will be done from quad centre..
  xOffset = tX;
  yOffset = tY;
  
  fTexXStart = (float)( i % 16 ) * fTexWidth;
  fTexYStart = (float)( i / 16 ) * fTexHeight;
  
  if( fCharCoords )
    glTranslatef( xOffset + ( x * g_fCharPixelWidth ), yOffset + ( y * g_fCharPixelHeight ), 0.0f);
  else
    glTranslatef( xOffset + x, yOffset + y, 0.0f);
  
  // PDS: Tighten up the source vertex so we don't pick up bits of the previous character..
  //
  // PDS: TODO: This should only be calculated once somewhere when switching fonts to speed things up!!
  //fTexXStart = fTexXStart + ( fOnePixelWidth / 2.0f );
  //
  // PDS: This is absolute rubbish!! The below hacks work OK when the font is scales up 4x but they don't render properly when using a 1:1 ratio
  fTexXStart = fTexXStart + ( fOnePixelWidth * 1.5f );
  fTexYStart = fTexYStart - ( fOnePixelHeight * 1.2f );
  
//  fTexHeight -= fOnePixelHeight;
  
  //  float fTexHeightNoSpacing = 0.0f;
  //  fTexHeightNoSpacing = ( fSourceHeight * fTexHeight ) / ( fSourceRowSpacing + fSourceHeight );
  //  fTexHeight = fTexHeightNoSpacing;
  
  GLfloat *pDst = dstVertices;
  GLfloat *pSrc = srcVertices;
  
  pDst = AddVertex3( pDst, -tX, -tY );
  pDst = AddVertex3( pDst,  tX, -tY );
  pDst = AddVertex3( pDst,  tX,  tY );
  
  pDst = AddVertex3( pDst, -tX, -tY );
  pDst = AddVertex3( pDst, -tX,  tY );
  pDst = AddVertex3( pDst,  tX,  tY );

  pSrc = AddVertex2( pSrc, fTexXStart, fTexYStart );
  pSrc = AddVertex2( pSrc, fTexXStart + fTexWidth, fTexYStart );
  pSrc = AddVertex2( pSrc, fTexXStart + fTexWidth, fTexYStart + fTexHeight );
  
  pSrc = AddVertex2( pSrc, fTexXStart, fTexYStart );
  pSrc = AddVertex2( pSrc, fTexXStart, fTexYStart + fTexHeight );
  pSrc = AddVertex2( pSrc, fTexXStart + fTexWidth, fTexYStart + fTexHeight );
  

  glEnableClientState(GL_VERTEX_ARRAY);
  glEnableClientState(GL_NORMAL_ARRAY);
  glEnableClientState(GL_TEXTURE_COORD_ARRAY);
  
  glVertexPointer(   3, GL_FLOAT, 0, dstVertices );
  glTexCoordPointer( 2, GL_FLOAT, 0, srcVertices );
  glDrawArrays( GL_TRIANGLES, 0, 6 );

  glDisableClientState( GL_VERTEX_ARRAY );
  glDisableClientState( GL_VERTEX_ARRAY );
  glDisableClientState( GL_TEXTURE_COORD_ARRAY );
  
  glPopMatrix();
  
  // PDS: Unbind texture, bind to default
  glBindTexture( GL_TEXTURE_2D, 0 );
  glEnable( GL_BLEND );
}



RE: 2D Font Rendering - OneSadCookie - Apr 12, 2011 11:03 AM

tried using NEAREST filtering for your texture?


RE: 2D Font Rendering - SparkyNZ - Apr 12, 2011 11:46 AM

SadCookie - you've done it again! Yes, GL_NEAREST in the tga.cpp file sorted it out! Perfect pixels at 32x32, 16x16.. only a bottom line missing when it drops to 8x8..

..and if anybody dares to use my code snippet in the future, add this line before rendering the character:

Code:
fTexHeight = ( ( ( 1.0f / fCharsPerCol ) / 9.0f ) * 8.0f );

I guess its time to optimise this horrible code now. :-)