[World] New font tricks
Daniel Phillips
phillips at phunq.net
Sun May 27 21:58:43 PDT 2012
Progress on typeface rendering continues.
Glyphs and text string rendering now uses 3D basis vectors for orientation,
which gives it the ability not only to render precisely positioned text, but
also allows general scaling, rotation and 3D orientation. In general, affine
transformations with perspective are now supported. Font24.png demonstrates
this.
Basis vectors are pretty cool. To render text standing up on the ground plane
at one meter per texel, you simply use the X and Z unit vectors as the
rendering basis. To render at a more reasonable size, scale down the basis
vectors to roughly the dimensions of one screen pixel.
Rendering text in 3D orientation is mainly a curiosity. The primary use case
coming up is screen-aligned, constant size text that tracks some 3D position.
To do the screen-aligned part, the text is rendered using an orthogonal
projection mapping the screen pixel per pixel, with the X and Y unit vectors
as the basis. By telling Freetype to use "dots per inch" of 72, the mapping
becomes: one point equals one texel equals one pixel. This nice relation helps
a great deal with both accuracy and sanity, otherwise life in a sea of strange
typographical units can get frustrating indeed.
Rendering screen-aligned text according to a 3D position is easy. The object
is to make the text rendered in the orthogonal projection appear as if it were
rendered as a billboard in the perspective projection. All we need to do is
transform the 3D world position of the text into screen coordinates using the
current view and projection matrices, giving the pixel coordinates for the
lower left text origin.
But we also need to take care of one nasty problem: 3D text must work
correctly with the depth buffer so that the text properly hides objects behind
it and is hidden by objects in front of it. Unfortunately, he depth buffer
mapping for an orthogonal projection and for a perspective rendering are not
at all the same. We need to render the text in the orthogonal projection at a
distance that produces exactly the same values in the depth buffer as if we had
rendered it in the perspective projection. Wow, how on earth do we know what
that distance should be? At least, the entire text is rendered at the same
distance from the eye, otherwise I doubt it would even be possible to match
the depth mapping between the two projections.
Calculating the correct render depth turned out to be awfully hard.I am not a
great mathematician - far from it - but I try to make up for it in other ways.
After initially failing to deduce the formula from the OpenGL specification, I
discovered it by experiment. I created a test bed to display the text at some
randomly chosen depth, then move my viewpoint forward and back until the text
ends up hidden by one object and hiding the other. At that point I knew the
distance from my eye to the object, and that my randomly chosen number was the
correct value to put into the depth buffer at just that distance. In this way I
found some specific points on the curve, including the general range that the
the number should be in. Should it be positive or negative? (Negative) Should
it be big or small? (Usually small.) Close to zero or one? (Close to one.)
Varying linearly with the world distance? (No, varying linearly with the
inverse of the world distance.) Putting those clues together I eventually
concocted a formula that always translates the perspective depth exactly to
the correct orthogonal depth. Once I had the formula, it was straightforward
to go back to the specification and verify that it is in fact the correct
formula. So that is how I did that, and why I did not get a lot of sleep last
night.
While I was in there, I added Unicode support for text strings, see
"font26.png". This phrase means "happiness" in Japanese, using one Kanji and
two Hirigana characters. Notice how the text nestles itself between the two
spheres, just as it should. Now you know a little about the kind of effort that
needs to go into making such a seemingly simple thing work properly.
There are still a few more steps before the new text renderer goes into
service. I need to implement a platform independent interface to the font
loader so that nobody needs to specify actual file names for font files.
Alternatively I could embed a basic font file in the distribution, which is
allowed legally because these fonts are all covered by Free licenses. But the
files are not tiny, for example, about 140K for Liberation Sans Regular, which
will be the basic World Welder system font. And we want to have lots of fonts.
I might end up embedding one font file in the tarball anyway, but for now I
will implement the Freetype Cache library interface, so fonts can be specified
in terms of descriptive terms instead of exact names. This library also
optimizes repeated access to many fonts from many programs, so startup time
should be improved by not needing to read the font file from disk as often.
Then I need to ruggedize the code and protect the OpenGL state that the font
render uses. Finally, the whole good sized piece of code, needs to be
"promoted" from its testbed into the World Welder runtime library. Phew, a lot
of work already and still more to do. All in the name of text display that
doesn't hurt your eyes. That is more than enough reason in my opinion.
Regards,
Daniel
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://phunq.net/pipermail/world/attachments/20120527/8178fd6c/attachment-0001.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: font24.png
Type: image/png
Size: 106361 bytes
Desc: not available
URL: <http://phunq.net/pipermail/world/attachments/20120527/8178fd6c/attachment-0002.png>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: font26.png
Type: image/png
Size: 76758 bytes
Desc: not available
URL: <http://phunq.net/pipermail/world/attachments/20120527/8178fd6c/attachment-0003.png>
More information about the World
mailing list