OpenGL text rendering anti-aliasing anomality

Started by
8 comments, last by Erik Rufelt 10 years, 1 month ago
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:
zone1_3.png
zone2.png
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...
Advertisement

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. ;)

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

aa_smog.png

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.

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?

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);

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:

fonttest.png

It looks fine.

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

fontopaque.png

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.)

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:

whiteonwhite.png

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...

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.

fontworking.png

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

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.

This topic is closed to new replies.

Advertisement