Jump to content

  • Log In with Google      Sign In   
  • Create Account

OpenGL text rendering anti-aliasing anomality

Old topic!

Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
9 replies to this topic

#1   Members   -  Reputation: 1072


Posted 18 February 2014 - 08:23 AM

I am trying to render text to screen using Open GL.
All tutorials I found were for iOS, and I am developing for OS X, and I don't want to artificially include UI* just for this purpose, unless someone gives me convincing arguments.
This is the method I am using, as it is the only one I managed to make work:  
NSColor *background = [NSColor colorWithDeviceRed:0.0f green:0.0f blue:0.0f alpha:0.0f];
NSColor *textColor = [NSColor colorWithDeviceRed:1.0f green:1.0f blue:1.0f alpha:1.0f];
stringAttributes = [NSMutableDictionary dictionary];
[stringAttributes setObject:[NSFont fontWithName:@"trench" size:64] forKey:NSFontAttributeName];
[stringAttributes setObject:textColor forKey:NSForegroundColorAttributeName];
[stringAttributes setObject:background forKey:NSBackgroundColorAttributeName];
NSRect myTextRect = [string boundingRectWithSize:NSMakeSize(1000.0f, 0.0f) options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingOneShot | NSStringDrawingTruncatesLastVisibleLine) attributes:stringAttributes];
myTextRect.origin.x = 0.0f;
myTextRect.origin.y = 0.0f;
width = ceil(myTextRect.size.width);
height = ceil(myTextRect.size.height);
textureBuffer = malloc(sizeof(int unsigned)*(height*width));
memset(textureBuffer, 0, 4*height*width);
NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:(char unsigned **)&textureBuffer pixelsWide:width pixelsHigh:height bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO colorSpaceName:NSDeviceRGBColorSpace bitmapFormat:0 bytesPerRow:4*width bitsPerPixel:32];
NSGraphicsContext *ctx = [NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap];
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext:ctx];
[string drawWithRect:myTextRect options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingOneShot | NSStringDrawingTruncatesLastVisibleLine) attributes:stringAttributes];
[NSGraphicsContext restoreGraphicsState];
The result is a back buffer with the text I want.
Then I need to regenerate the textures when the user resizes the window.  I have this simple reshape method:
- (void)reshape {
    [super reshape];
    float w = [self frame].size.width;
    float h = [self frame].size.height;
    glViewport(0, 0, w, h);
    for (id o in guiObjects) {
        if ([o isKindOfClass:[STAString class]]) { // Yes I am going to make a protocol eventually
            [o regenerate:NSMakeSize(w, h)];
This reshape method does not account for scaling of the font yet, so whatever the size of the window, the text stays the same size.
The scaling is done by deleting the texture, and making a new one on the fly.
The problem is...  As I told, the text stays the same since I am not scaling the text with window size.  Yet, the text shows minor antialiasing artefacts that seems to have two patterns that changes with different zones of vertical sizes.  With zones I mean there is three states the font renders to: one where the first 'o' looks a bit odd, then one with the 'W' looking a bit odd, then back to the first sitation again.  The middle zone seems to be around 50-10 pixels as I increase the height of the window.
Here are pictures to show:
It looks ugly.  Anyone having any help, or tips how to make text look better?
Please also grab the opportunity to pick on my coding practise so I can get better.  I am always looking to perfect my skills.
EDIT: Forgot tags... Corrected title...

#2   Members   -  Reputation: 1072


Posted 18 February 2014 - 10:04 AM

Ah, geez...   How silly can you be...  I forgot to use the centre of a pixel when I positioned the text...  Problem solved, sorry to have disturbed you.  ;) 

#3   Members   -  Reputation: 1072


Posted 18 February 2014 - 11:26 AM

I have another problem, now, though...  I don't know how to fix that.  When I output smaller text, it fades into aa-smog...  




I tried to render the text into a texture 4 times bigger, and quarter it when I positioned it on the screen.  It looked exactly the same.  That makes me think my fragment shader is to blame, but I am not doing anything weird there...

#version 150 core

in vec4 color;
out vec4 outFColor;
in vec2 textureCoordinates;
uniform sampler2D sampler;

void main(){
 outFColor = texture(sampler, textureCoordinates) * color;

Any suggestions?


Edit: Turning off blending, makes it more visible, but it still looks awful.

Edited by aregee, 18 February 2014 - 11:30 AM.

#4   Members   -  Reputation: 3018


Posted 18 February 2014 - 12:12 PM

I have no experience with OS X, but why are you not rendering the text at the proper size and then displaying it 1:1 in opengl?

#5   Members   -  Reputation: 1072


Posted 18 February 2014 - 01:33 PM

But that is what I am doing.  The resizing thing was something I tried to investigate the problem.  It didn't do any difference.


I render to a back buffer having width and height.  Attach that to a texture, having corner coordinates (0,0), (0,1), (1,1), (1,0).  Plotting it to a "Quad" having same width and height as the back buffer.  


The final height and width is converted to screen coordinates by calculating a "scale" factor:

    GLfloat xScale = 2 / size.width;  // Size = screen size
    GLfloat yScale = 2 / size.height;

This gives me the size of each pixel on the screen.  (Screen coordinates ranging from -1 to 1 = 2.)


If I want to put something at position 100, I just add a half and multiply by xScale and yScale for x and y respectively.  Yes, I know I can just send a matrix, but I make things to test first, then refine by refactoring a better solution once I know how I want to solve a problem.


Edit: Here is the actual code:

    GLfloat xScale = 2 / size.width;  //Size.width and height is Screen
    GLfloat yScale = 2 / size.height; //Just width and height is Texture
    GLfloat posX = position.x + 0.5f;
    GLfloat posY = size.height - height - position.y + 0.5f;
    GLfloat x1 = -1.0f + (posX * xScale);
    GLfloat y1 = -1.0f + (posY * yScale);
    GLfloat x2 = -1.0f + ((posX + width) * xScale);
    GLfloat y2 = -1.0f + ((posY + height) * yScale);

Edited by aregee, 18 February 2014 - 01:37 PM.

#6   Members   -  Reputation: 1072


Posted 19 February 2014 - 01:36 PM

Ok, since it seems no one here is able to help, here is what I have found:


I tried to write the buffer with the bitmapped text to file:




It looks fine.


Then I tried to set the alpha channel to 1.0f for the background color, making the text fully opaque.




This looks ok-ish.  Maybe I just need to change for a more readable small font.


The problem is that I can't write opaque text.   I can't have a dark blob cancelling out the background graphics.  Is there really no one here that can help?


Edit: (The saved file and the output from my OpenGL app looks the same to the best of my eyes, so there is no positioning or scaling problems.)

Edited by aregee, 19 February 2014 - 01:46 PM.

#7   Members   -  Reputation: 1072


Posted 21 February 2014 - 11:28 AM

I will close this now.  Seems no one here have any hints at all.


I tried to write text white on white, since white on black looks much worse then black on white.  This is what I got:




I really shouldn't be able to see anything here, still I have anti-aliasing shadows in the text.  I am assuming writing strings using NSString is bad bad bad...  Time to look into other ways of writing text...

#8   Members   -  Reputation: 1072


Posted 22 February 2014 - 04:07 PM

Just to update in case someone else is struggling with the same as I am (although I doubt...)


In my previous post, I had a hunch about what was going wrong.  I made the following test:

    char unsigned *byteBuffer = (char unsigned *)textureBuffer;
    for (int i = 0; i < width * height; i++) {
        int r = byteBuffer[i * 4 + 0];
        int g = byteBuffer[i * 4 + 1];
        int b = byteBuffer[i * 4 + 2];
        int a = byteBuffer[i * 4 + 3];
        a = round((r+g+b)/3);
        byteBuffer[i * 4 + 0] = 255;
        byteBuffer[i * 4 + 1] = 255;
        byteBuffer[i * 4 + 2] = 255;
        byteBuffer[i * 4 + 3] = a;

It basically averages the color from the [NSString drawWithRect...] and replaces the alpha with the average of the colours.  Then I set the RGB colours to 255.


This surprisingly worked.  By no means an elegant solution, but it works for now.  I am still looking for a better solution, but for now this will do.



#9   Members   -  Reputation: 2540


Posted 22 February 2014 - 05:22 PM

I suspect that sub-pixel antialiasing is causing your problem.

#10   Crossbones+   -  Reputation: 5889


Posted 22 February 2014 - 08:38 PM

Your white-on-white text indicates that the the string texture has premultiplied alpha (GL_ONE, GL_ONE_MINUS_SRC_ALPHA) and you draw it with (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) instead.

Old topic!

Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.