I'm drawing text in OpenGL using FTGL library and everything works just fine, however I would like to add shadow to the text. What I tried is drawing same text with black color and then draw text above that with normal color like this (pseudocode):
glColor3f(0, 0, 0); // outline color
DrawText(x-1, y-1, str);
DrawText(x+1, y-1, str);
DrawText(x+1, y+1, str);
DrawText(x-1, y+1, str);
glColor3f(1, 1, 1); // primary color
DrawText(x,y,str);
But I have to draw the text 5 times and it still does not look very good.
I would like to get something like on the screenshot
There are probably a lot of ways to achieve that - some with higher quality than others.
Here's what I would do:
render the text to an in-memory grayscale pixmap.
perform a gaussian blur on it (probably using a fast library such as QImageBlitz or ImageMagick). The blur radius should be about 2-3px.
apply a steep tone-curve to the blurred image, so the luminance range [0.0, 0.9] is mapped to nearly 0.0. This makes it stop being blurry, and the result is a "fattened" version of the text. The tone curve should look something like this:
render that as the shadow, in black (using an appropriate blending mode to emulate alpha-blending). Then render the regular yellow text on top of it (with a small offset of your choice).
Also, you can use different tone-curves depending on how soft a shadow you want. A linear tone-curve would give a very soft shadow.
I am usually doing it this way:
set color to semitransparent black, eg (0,0,0,0.5)
draw the text in all the nine directions (move to sides, and then diagonally)
draw the fg text.
It looks quite good, and you can speed it up with render list and translations.
see here: http://i.stack.imgur.com/Dh68y.png
Related
I am using GL_LINE_STRIP and glLineWidth to draw lines.
However, this leads to gaps between the single, straight segments of the strip.
I had mitigated the problem by using Catmull-Rom Splines and thus the segments where smooth enough to not notice the gaps anymore:
But now I noticed the gaps are different depending on the OpenGL implementation. Mesa introduces larger gaps than my graphic card, notice the gaps in the upper part and how the lower part with much smaller segments is noticeably darker due to more gaps:
Please note that image 1 and 2 are the same render code, the opacity is 255 in both cases, just the used opengl32.dll differs.
I then added the drawing of every joint as point:
glBegin(GL_LINE_STRIP);
for (auto p : interpolatedPoints) {
glVertex2f(p.x, p.y);
}
glEnd();
glBegin(GL_POINTS);
for (auto p : interpolatedPoints) {
glVertex2f(p.x, p.y);
}
glEnd();
This works for opacity 255 but not if I want to reduce the objects transparency. What happens then, is that the transparent point overlays the transparent line strip, thus increasing the opacity especially in areas with very short strips:
Solution 1: Polyline quadstrip
Ditching GL_LINE_STRIP altogether and triangulate the line strip ourselves seems the solution here but this looks like a larger rewrite for me - either I need a new shader or I need to calculate the triangles.
Solution 2: Blending
Wanting to avoid the rewrite, I was wondering: can blending solve the issue? Currently I use
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
Is there a blending configuration which would prevent the points to add on the alpha channels of the lines? I tried some other constants here but I had no success. Please note also that the black background in the screenshots may not be black at all but contain other objects and textures which should be "correctly" overlayed by the semi-transparent line.
As a potential easy fix, you could try setting glHint(GL_LINE_SMOOTH_HINT, GL_NICEST) and see if that helps.
If you want your lines to look nice when drawn transparently, I suggest drawing all your lines onto a separate framebuffer than the rest of your scene, reusing the same depth buffer, and with full opacity. Then draw the lines framebuffer onto the rest-of-your-scene framebuffer with partial transparency.
I've implemented FreeType in my program, I can draw text with colors and style (Bold, Italic, Underline).
Now I would like to make an outline effect on my text. How can I do it?
(source: googlecode.com)
I've tried to draw the text two times, one larger in black in background and the second in white on foreground, result was wrong.
I've tried to draw the text two time, one in bold and the second in one on foreground, here again the result was wrong.
I would like to make a test again : Draw the "background" text in "outline" mode and the foreground text in regular mode. What do you think about it?
(source: googlecode.com)
Rendering text with 2 passes is the key, but you have to position the text correctly.
First you should render whole outline and then on top of it render the text.
Rendering the outline:
// initialize stroker, so you can create outline font
FT_Stroker stroker;
FT_Stroker_New(library, &stroker);
// 2 * 64 result in 2px outline
FT_Stroker_Set(stroker, 2 * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
...
// generation of an outline for single glyph:
FT_UInt glyphIndex = FT_Get_Char_Index(face, glyphId);
FT_Load_Glyph(face, glyphIndex, FT_LOAD_DEFAULT);
FT_Glyph glyph;
FT_Get_Glyph(face->glyph, &glyph);
FT_Glyph_StrokeBorder(&glyph, stroker, false, true);
FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, nullptr, true);
FT_BitmapGlyph bitmapGlyph = reinterpret_cast<FT_BitmapGlyph>(glyph);
// blit the glyph here on your target surface.
// For positioning use bitmapGlyph->left, bitmapGlyph->top
// For iteration over the glyph data use bitmapGlyph->bitmap.buffer, bitmapGlyph->bitmap.width, bitmapGlyph->bitmap.rows, bitmapGlyph->bitmap.pitch.
Next you have to render the text itself on the same data you've blitted the outline. Use the code above, but remove the FT_Glyph_StrokeBorder(&glyph, stroker, false, true); line.
This way you will have the text on top of an outline.
To achieve this "Cartoon" text effect you will have to do 4 passes: 3 outlines + 1 text. Texturing or applying a gradient should be done during the blitting phase.
Draw the text, then do a second pass over every pixel that was not fully coloured in. For each of those pixels calculate how far away it is from the nearest coloured pixel. If it's less than X where X is the desired width of your outline, colour it in using your outline colour.
It can be slow to do this for large text but it can be optimised and the results cached to make it run acceptably fast. This method allows complete freedom for all kinds of outline and drop shadow effects.
Since GL_LINE_SMOOTH is not hardware accelerated, nor supported on all GFX cards, how do you draw smooth lines in 2D mode, which would look as good as with GL_LINE_SMOOTH ?
Edit2: My current solution is to draw a line from 2 quads, which fade to zero transparency from edges and the colors in between those 2 quads would be the line color. it works good enough for basic smooth lines rendering and doesnt use texturing and thus is very fast to render.
So, you want smooth lines without:
line smoothing.
full-screen antialiasing.
shaders.
Alright.
Your best bet is to use Valve's Alpha-Tested Magnification technique. The basic idea, for your needs, is to create a texture that represents the distance from the line, with the center of the texture being a distance of 1.0. This could probably be a 1D texture.
Then using the techniques described in the paper (many of which work with fixed-function, including the antialiased version), draw a quad that represents your lines. Obviously you'll need alpha blending (and thus it isn't order-independent). You use your line width to control the distance at which it becomes the appropriate color, thus allowing you to make narrow or wide lines.
Doing this with shaders is virtually identical to the above, except without the texture. Instead of accessing a distance texture, the distance is passed and interpolated from the vertex shader. For the left-edge of the quad, the vertex shader passes 0. For the right edge, it passes 1. You multiply this by 2, subtract 1, and take the absolute value.
That's your distance from the line (the line being the center of the quad). Then just use that distance exactly as Valve's algorithm does.
Turning on full-screen anti-aliasing and using a quad would be my first choice.
Currently I am using 2 or 3 quads to do this, it is the simpliest way to do it.
If line thickness <= 1px, then you need only 2 quads.
If line thickness > 1px, then you need to add third quad in the middle.
The fading edge quads thickness must not change if the line thickness >= 1px.
In the image below you can see the quads with blue borders. White color means full opacity and black color means zero opacity (=fully transparent).
I am drawing lots of small black rectangles to the white screen and as they move about and zoom it doesn't look very graceful.
How can I draw them so if the edge lies between pixels, the pixels will be grey rather than black?
Sub-pixel rendering is actually more complicated than regular anti-aliasing. If you're using a display with pixels in RGB format, then a gray shape ending halfway through a pixel might be rendered as yellow or as cyan, depending on which side of the pixel the shape lies. This can look strange when the resulting image is not drawn in native resolution or on a display with a different layout than expected, but otherwise it can look quite nice.
Here is a sample of sub-pixel rendering applied to text; the left panel is color, the center panel is the displayed version of the color, and the right panel is the perceived brightness. Notice that accuracy in hue is exchanged for accuracy in brightness.
One approach might be to render each channel separately, each slightly offset in rendering space by the appropriate amount, so that the combined image is in full color. Each of the channels must be the same resolution as the resulting image; each channel is rendered with anti-aliasing the same way as the original would have been, except the other colors are ignored. Once the channels are created, they can be combined with simple addition. I don't know of a "pure" solution like the glEnable/glHint versions available for normal anti-aliasing, but it may exist or may in the future.
glEnable(GL_SMOOTH); should do it.
What you are effectively asking for is anti-aliasing,and there are numerous ways of doing it. One way is summarized in this gamedev topic: http://www.gamedev.net/topic/107637-glenablegl_polygon_smooth/.
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glHint(GL_POINT_SMOOTH, GL_NICEST);
glHint(GL_LINE_SMOOTH, GL_NICEST);
glHint(GL_POLYGON_SMOOTH, GL_NICEST);
glEnable(GL_POINT_SMOOTH);
glEnable(GL_LINE_SMOOTH);
glEnable(GL_POLYGON_SMOOTH);
I have a font texture which I use in order to draw text on top of my OpenGL scene.
The problem is that the scene's colors vary so much that any solid color I use is hard to read. Is it possible to draw my font texture with inverted colors?
A call to glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); would just draw it with a solid color, A call to glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO); would draw it inverted, but would disregard the alpha values of the texture so you'd just see an inverted rectangle instead of the letters.
What I need is something equivalent to
glBlendFunc(GL_SRC_ALPHA * GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA);
Can this be achieved somehow without using shaders ?
Following dave's comment:
Instead of using a GL_ALPHA texture I generate a GL_RGBA texture where each pixel is equal:
(alpha, alpha, alpha, 0xff) (this is a greyscale image instead of a luminance image).
Then I use this texture with:
glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR);
The result is: (1 - dest_color) x "src_alpha" + dest_color x (1 - "src_alpha")
which is exactly what I needed.
Copy the subsection of the screen you want to overwrite, you can use the CPU to invert this copied portion. Draw your newly made inverted texture to the screen using the text as a mask.
Note that this is slow, there's no doubt that shaders would be much faster.