TextRenderer.draw3D scaleFactor

Hi,

I have a couple of quick questions about the TextRenderer class.

  1. how do you use the scaleFactor argument?

I have a font of size 72 and then I have to use a .01f scale to view it on the screen. I found this just by experimenting, I don’t actually understand the logic-- can someone clarify?

Since I’m placing text inside a series of rectangles, I want to be able to calculate the appropriate scale to fit a particular width in 3D space.

I can’t see how to do this from the textCube demo, or the javadocs.

  1. how can I determine the best font size for a particular position?

If I have a large font (72 or higher say) it looks great when it’s close up, but if I zoom away from it, it looks bad. Likewise, if I have a smaller font it looks good far away, but blurry when I zoom into it.

Does the textRenderer use mipmapping? Or does someone have some idea of how I could make the text look better as it moves around the screen and especially as it moves forward and backward on the z-axis. Also, I was thinking I could swap TextRenderers at different distances to get different sized fonts, but sometimes changes a font size changes the look of the font slightly as well.

Thanks,

spiraljetty

These are good questions.

The scaleFactor argument is applied equally to the width and height of the quad upon which the bitmapped text is rendered. The size of the quad in world coordinates is equal to the width and height of the bounds of the string in pixels. A string whose Rectangle2D from TextRenderer.getBounds() reports a width of 200 and a height of 50 pixels will be rendered on a quad of width 200 units and height 50 units in world coordinates.

The current JOGL source tree (and the current binary build on the JOGL home page, labeled “RC4”, but which won’t end up being the official bits – one more bug fix still to go in) has mipmapping support in the TextRenderer. However, as you’ve found, just scaling down a 72 point font may not look as good as using a smaller font at a distance.

My recommendation would be to have a few preferred font sizes in your app and a few associated TextRenderers, and compute at run time which one to use based on how much screen space will be taken up by the quad you’re going to render the text on to.

Hi, thanks for the reply.

This may not be the place to ask this question. But I have been thinking about your recommendation and I am not sure how to determine dynamically how much screen space in pixels a quad in world coords will take up! That is if I have a centered quad from (-2f,-2f,0f) to (2f,2f,0f) how do I figure out where it lives in screen coordinates. Obviously this is dependent on screen size, the projection matrix, and the camera position!

[quote]My recommendation would be to have a few preferred font sizes in your app and a few associated TextRenderers, and compute at run time which one to use based on how much screen space will be taken up by the quad you’re going to render the text on to.
[/quote]
Ok-- just did some quick research-- it looks like gluProject is the appropriate method. Has anyone used this before? I don’t quite understand what arguments it want…

Sorry if this is more an OGL question than a JOGL question. But it seems like it might be helpful to know the answer to this for different situations.

Thanks!

Note that you may be able to just use the distance of your text from the camera as a rough approximation of which font size to use.

Hi, thanks for your help Ken. I definitely appreciate it.

Fonts just look bad unless they are rendered at or very close to the same size as their pixel value. At least with regular filtering. Mipmapping might help. In your comment you seem to say that there will not be a mipmapping option in the next release. I vote that there should be.

I’ve come up with what I think is a workable approach. Actually two approaches-- one uses drawText3D and one uses drawText.

approach 1:

immediately after making the main frame visible, but before initializing the GL eventListener I call this method:

 public void makeFonts(String fontName)
  {
    Graphics2D g2D = (Graphics2D) frame.getGraphics();
    FontRenderContext frc = g2D.getFontRenderContext();

    for (int i = 18; i < 400; i+=4) //or whatev...
    {
     fonts.add(new FontInfo(new Font(fontName, Font.PLAIN, i), frc));
    }
}

Of course you can alter the loop to use less fonts. But even if I create 400 fonts the application loads immediately. I haven’t tested if there is any performance/memory penalty by loading up so many text textures.

FontInfo is basically a struct which holds information about the font and also the TextRenderer for this font as well.


public class FontInfo
{
  public Font font;
  public FontRenderContext frc;
  public Rectangle2D bounds;
  public float w;
  public float h;
  public TextRenderer textRenderer = null;
  
  public FontInfo(Font font, FontRenderContext frc)
  {
    this.font = font;
    this.frc = frc;
    this.bounds = font.getMaxCharBounds(frc);
    this.w = (float) (bounds.getWidth());
    this.h = (float) (bounds.getHeight());
  }
   

Now within my rendering loop I have a method which determines the appropriate TextRenderer to use based both on a projection of the width in world space to the pixel width in screen space and also the number of characters to expect within this width. Non-monotype fonts of course have different widths for different characters of course. So I use the maximum width (using “(font.getMaxCharBounds(frc)).getWidth()” to calculate.

The projection uses glProject, which is probably pretty standard and I put in another posting.

So, I have a pixelWidth of a character which I compare against the max width of a character using the font and fontsize. Using this I can pick the appropriate font for the particular coordinates in world space.

Then I create a scale value for the draw3D function and use it like so:

float scaleVal = (worldSpaceWidth / numCharsToFitIntoWorldSpaceWidth) / maxWithOfCharForFontWeJustChose;
textRenderer.draw3D("text", worldX, worldY, worldZ, scaleVal);

This works great. Two slight issues. There is slight fuzzing when between fonts. That is, if I have a TextRenderer for font size 18 and another for font size 24, it will be slightly fuzzy when it can’t find a render for font size 22.25 or whatever and chooses the closest one it can. I think mipmapping would solve this. The other issue is that fonts look slightly different even when just one size apart, especially in the lower sizes, like 9 to 10. Jumping between these sizes is a bit jarring. So there’s a tradeoff between bluriness/aliasing and jumpiness. At larger sizes it doesn’t look as bad.

approach 2:

This is a bit simpler. I find the appropriate sized font in the same way as above. Then I find the screen coordinate of where I want to put the text in pixels using glProject. Then I use textRenderer.draw method using those pixel coordinates.

This I guess is a bit cleaner in that the text will always look sharp, but there is no way to get in-between values and any switch to a differently sized font is abrupt.

Ok this is pretty wordy. Anyhow, if anyone has any other approaches I would be interested in hearing them. It definitely seems that font sharpness is dependent on the pixel width of the area you are drawing on. Also, this assumes that the font is always straight-on. If the z-coord wasn’t the same across the object then there would be an issue with this method.

-spiraljetty

First, mipmapping support for the TextRenderer is already in JOGL. Look at the current nightly build and TextRenderer(Font font, boolean mipmap).

Your approach 1 seems basically good but I would encourage you to use the TextRenderer.getBounds() method on the entire string rather than computing your scale factor based on the maximum width of a particular glyph in the font. I think that will reduce the jarring when switching between fonts of different sizes.

Ok, I am still using rc3. I’ll get the latest builds and test mipmaps. I’m not using getBounds() to calculate because I want to make sure that any text within a certain width is using the same font size. If I base it off of getBounds for the entire string then a string like “11111” might use a different font size than “00000” when not using a monotype font… Using the getMaxCharBounds ensures that the string of ones and string of zeros will use the same font even though their characters do not take up the same amount of space.

(an hour later…)

–just tried mipmaps in rc4-- looks perfect! thanks