zShapes - Resolution Independent Curve Rendering

Hi everyone, this is the first version of zShapes (~3.5MB, includes samples).

WHAT IT IS [BRIEFLY]

zShapes is a tool that allows you to render resolution independent vector graphics in real-time on your GPU. The tool can convert any Java2D shape to a set of triangles that can later be rendered with OpenGL. Antialiasing is also supported.

HOW IT STARTED

zShapes started as a little weekend project. I had been impressed by UlfJack’s Thinlet port to LWJGL, except one thing: it was lacking fonts. So I was thinking “now he’s going to have to do the whole texture thing, what a bunch of crap that technique is”. He was going to do it, I’m doing it in Marathon, everyone else is doing it. My next thought, “it should be possible to do this right with today’s GPUs”. So I decided that it was going to be a “font rendering” weekend. Turned out to be a month of course. :wink:

WHAT IT IS

After an exhaustive search of my paper library, I came across a really interesting paper:

Resolution Independent Curve Rendering using Programmable Graphics Hardware, by Charles Loop and Jim Blinn

The paper describes a technique that can be used to render any quadratic Bezier curve using a single drawing primitive: a triangle and a simple function that is evaluated per-pixel. Basically zShapes is an implementation of this technique. It is a simple library at it’s core, but is accompanied by an editor, that provides a UI to do the shape->triangle conversion, and a viewer, that renders the shapes with OpenGL.

FEATURES [Editor]

The primary function in the Editor is the shape triangulation. You give it any Java2D shape and it outputs the generated triangles. It’s not a simple delaunay triangulation, a lot of work is done to remove overlaps and clean-up the mesh, and a lot of triangle breaking & triangle additions are needed to support the antialiasing implementation.

There are two ways to produce work for the Editor:

  • A simple dialog allows you to create some text using any font supported by the system. The text outline (as a Java2D shape) is then converted to triangles.
  • Any SVG file can be imported and used as a Java2D shape. I use the Batik SVG Toolkit for this.

After a triangulation, the generated mesh can be exported to file.

The Editor has a few other features that were added to make it more user friendly:
- Easy navigation/zooming
- A console that logs what the triangulator is doing
- Various overlays for debugging (shape overlay, points, statistics, etc)

FEATURES [Viewer]

The Viewer is an LWJGL application that renders the generated meshes. It provides basic navigation/zooming and support for toggling antialiasing. What’s important though is the rendering implementations. The technique is supposed to work with fragment shaders, but I’ve implemented the following variations:

With no antialiasing
- NV_register_combiners (GeForce 3 or better)
- ATI_fragment_shader (ATI 8500 or better)
- GLSL fragment shaders (GeForce FX, ATI 9500 or better)

With antialiasing
- GLSL fragment shaders, using shader derivatives (GeForce FX, ATI X1x00 or better)
- GLSL fragment shaders, using texture derivatives (ATI 9x00, ATI Xx00)

It was a little difficult to overcome the accuracy limitations of NV_RCs and ATI_FS, but it’s usable now by many more users.

ADVANTAGES

  • It’s a resolution independent technique (duh). You can zoom in as much as you want and it never produces aliasing. The only limitation is GPU accuracy, that can cause some artifacts in extreme cases (when zooming, also the non-GLSL paths are more sensitive).
  • Rendering is very fast. Also, if compared to the alternative (massive triangle tesselation), there’s a very nice balance between vertex and fragment processing.
  • It has tiny memory overhead. The meshes are generally low-poly (depending on the shape of course) and the geometry data is only 3 floats per vertex.
  • Antialiasing is done in the shader using derivatives. That means it doesn’t need multisampling to work, neither POLYGON_SMOOTH. It’s very fast too.

DISADVANTAGES

  • No shape coloring/shading is currently supported. The renderer only generates an alpha value (in shape or out of shape). Basic shading could be very easily added though (colors, gradients, etc).
  • The Editor triangulation is quite slow.
  • Cubic curves are currently approximated with quadratics. Depending on the error threshold, many unnecessary triangles may be generated.
  • Antialiasing is quite poor is certain cases.
  • The Editor currently runs on Windows & Linux only. MacOSX is also supported for the Viewer.

FUTURE WORK

There are a lot of improvements that can be done, but I don’t think I’ll spend much more time on this. Given enough interest, I could set up a project on SF for others to contribute, though. Given no interest, I’ll post the source here as-is. Anyway, my e-mail is spasi [at] zdimensions [dot] gr. The list:

Tool improvements
- The triangulation is currently done using Triangle, a powerful and robust delaunay triangulator, which is unfortunately written in C. This limits the Editor to Windows & Linux only. A Java port of Triangle, or any other decent Java-based triangulator would be nice to have. Also, it would more easily allow the library to be used at runtime.
- All the algorithms are almost brute-force and “linear” in design. Virtually every part of the implementation can be sped up and optimized memory-wise.
- The Editor currently misses a way to produce a “font map”, that is, a glyph list that can used to render any text in a GL application. I’ll do this soon anyway.
- Support for display lists and VBO should be added in the Viewer.
- Support for basic SVG coloring features may be added to the Viewer.
- It’s not much of library as-is. Both the triangulator and the renderers should be made more general.
- Support for JOGL rendering could be added.

Technique improvements
- The RICR paper also describes how to render cubic curves. My math skills are too limited for that though, I couldn’t understand it and also didn’t have time to understand it. It would be a great addition though. Especially for SVG shapes that are heavy on cubic curvers, a significant reduction in triangle count could be achieved (a tradeoff for a few more fragment shader instructions).
- I don’t think I’ve completely understood the way antialiasing is supposed to work. Basically external triangles are added to the mesh, but that has forced me to implement more than one hack to overcome quality issues (also the triangle count is increased unnecessarily). I may contact the paper authors about this.
- Subpixel antialiasing could be implemented.
- Another interesting technique that could be explored is GLSL Conics.

SCREENSHOTS (Editor | Viewer, click for hi-res)

A text example:

[tr]
[td]
http://www.zdimensions.gr/spasi/public/060901_zShapes/editor1_sm.jpg
[/td]
[td]
http://www.zdimensions.gr/spasi/public/060901_zShapes/viewer1_sm.jpg
[/td]
[/tr]

An SVG example:

[tr]
[td]
http://www.zdimensions.gr/spasi/public/060901_zShapes/editor2_sm.jpg
[/td]
[td]
http://www.zdimensions.gr/spasi/public/060901_zShapes/viewer2_sm.jpg
[/td]
[/tr]

That’s it, C&C welcome.

Sorry guys, I had no time for tests and the original zShapes didn’t work on Java 5.0. It is fixed now.

That’s really cool - works on my java 5.0.

Added support for ARB_vertex_program in the NV_RC & ATI_FS paths for cards with no GLSL support (usually laptops with ancient drivers). Special thanks to Akis.

thats really nice runs really good here on linux, jre5.

could it possibly be turned into a font rendering library that can be used in games or does it take too much resources to be used in game?

That’s the plan. General purpose font rendering is the primary target, but I’ll have to implement it first and see how good or bad it scales. I’ll need help to make it a truly general library though.

This is great stuff - I saw the paper while reading about Xgl and how thet might exploit current GPUs for accelerating desktop rendering. I wanted to implement it for our own games but never got around to do it (partly because of the GLSL requirement, which you seemed to have offset by a card generation or two).

  • elias

Made some progress with general text rendering:


http://www.zdimensions.gr/spasi/public/060901_zShapes/viewer3_sm.jpg

The above is 3 Lorem Ipsum paragraphs as well as some english, chinese, romanian, brazilian and greek text, all rendered at ~280 fps with fragment shaders + antialiasing.

Turned out quite nice, but there are many issues yet to be solved. I’ll post details some other day. I will also update the program .zip when I have a decent UI for the new functionality. Source code will be posted when most of the significant issues are solved and some performance optimizations made.

Sweet!
Actually this is quite similar to an issue I kept pushing down my todo list :wink: I’ll certainly give it a try when I catch up!

looking great and even better than before ;D

Implemented italicized, underlined and striked though text rendering. All three are dynamic (a single glyph source is used). The italic angle is also customizable (a simple shear ratio). Examples:

[tr]
[td]Original[/td][td]
http://www.zdimensions.gr/spasi/public/060901_zShapes/viewer4_sm.jpg
[/td]
[td]Italic[/td][td]
http://www.zdimensions.gr/spasi/public/060901_zShapes/viewer5_sm.jpg
[/td]
[/tr][tr]
[td]Underline[/td][td]
http://www.zdimensions.gr/spasi/public/060901_zShapes/viewer6_sm.jpg
[/td]
[td]Strikethrough[/td][td]
http://www.zdimensions.gr/spasi/public/060901_zShapes/viewer7_sm.jpg
[/td]
[/tr]

Neat stuff. ;D

However, in a game I tend to have bits of text floating around all over the place and at different layers. Isn’t this going to require lots of binding and unbinding of fragment programs and drag the performance down somewhat? Or am I overestimating the actual cost of switching between shaders?

The standard technique requires binding and unbinding of textures, so it has equivalent state changing cost. Also note that in order to switch fonts you don’t have to change anything (just call a different display list), whereas with the standard technique you still have to change the texture. Btw, the above examples, including italics, use exactly the same shader (non-italic is italic with a zero angle :)). Overall, state changing should not be a problem.

It still has several disadvantages that I will explain in depth in a later post, and it IS slower than using textures of course. But that’s totally acceptable considering what you get in exchange.

I always thought that a shader change was more expensive than a texture bind? It’d be great if you could detail some of the disadvantages, although from the quality I expect its a perfectly good tradeoff in most cases.

[quote=“Orangy Tang,post:14,topic:28237”]
It is, but not by much. Anyway, the relative cost of a state change is not important, but the pipeline stall caused by any state change, is. So, I’d argue that modern engines should struggle to minimize the number of state changes and not just order by the most expensive state. This can be achieved with slightly more complex shaders and techniques like pseudo-instancing (I’m using both).

[quote=“Orangy Tang,post:14,topic:28237”]
The disadvantages in a few words:

  • Low quality without AA
  • Low quality at small font sizes, even with AA (may be fixable in the AA case)
  • AA requires SM2.0 hardware
  • AA requires a texture bind on non-SM3.0 ATI hardware
  • Huge memory requirements for large unicode ranges (chinese glyphs are particularly heavy)
  • Display lists are memory heavy (lots of duplicate vertices) and cannot take advantage of post-transform cache optimization.
  • With VBOs indexing can be done (2.5 times lower memory requirements, even below the standard technique), but would require more state changes at runtime.
  • With indexed VBOs and large unicode ranges, either 32bit indices would have to be used (bad), or even more state changes would be required.

Depending on the developer needs, some of the above are not issues and some may be mitigated by decent tools and clever rendering techniques.

It sounds very impressive, but I have some questions:

  • I couldn’t find the thinlet port to lwjgl you mentioned in your first post on google or these forums, do you have a link? (as such a leightweight lwjgl gui sounds interesting!)
  • You also mentioned you found it disapointing that it used font rendering to textures, but isn’t this totally acceptable for use in games?
  • I understand it will be a generic shape engine? Will you support fillers / textures for shapes?

Anyway, keep up the good work!

thinlet port is being done by UlfJack, its not released yet, but i hear its very close to being finished.

Hi bitshit,

[quote=“bitshit,post:16,topic:28237”]
As kapta said, it’s going to be released soon. I’m eager to try it too.

[quote=“bitshit,post:16,topic:28237”]
The little demo I had tried didn’t have font rendering at all. It just gave me the initial inspiration to do something about it. And yes, font rendering with textures is totally acceptable for games and, as I said in my first post, I’m doing it in my engine.

[quote=“bitshit,post:16,topic:28237”]
Well, the primary target was to explore a new way to render real-time text. Thanks to the Batik library, SVG support was easy to add, so yes, it is a generic shape renderer*. But only the shape outline of an SVG graphic is used. Adding support for fillers/gradients/textures/whatever, or even supporting the whole SVG thing, would be a huge undertaking. AmanithVG is a great library that supports almost everything, but it’s commercial and it’s a C library.

Nevertheless, I’m hoping that the final library would make it easy for the users to add their own shading. Simple fills and gradients are easy, so I’ll probably provide some default implementations. More complicated stuff will have to be done at a higher level.

  • Btw, the triangulation “primitive” is a Java2D Shape. Anything that can be converted to a Java2D Shape is applicable, not only fonts and SVG graphics.

thanks for the info!

i checked out AmanithVG and it looks nice. Though it’s not clear to me how your implementation differs from theirs? they both seem to render SVG shapes & operations on the GPU using shaders (which is the openVG standard?)

i don’t know too much about this, but what would make textured shapes etc be so hard to implement? from the looks of it most of the work is already done…

[quote=“bitshit,post:19,topic:28237”]
There are big differences:

At the low level, my implementation takes advantage of the technique in the paper I mentioned in my first post, which allows rendering of a quadratic curve segment using a single triangle (resulting in significant triangle count reduction). AFAIK, AmanithVG does full tessellation. Which is understandable, considering the hardware they target.

At the high level, well, my implementation does not even try to understand the SVG graphic. I only added support for SVG to show the potential of the technique. On the other hand, AmanithVG has amazing support for SVG rendering: fills, strokes, paths, patterns, scissoring, filters, blend modes, etc.

Two good examples are the fort.svg and lwjgl.svg from the samples folder. Open them and press Ctrl+O to see the shape overlay (rendered with Batik). The information about the internal shapes and shading exists in the SVG files, but I’m not using it in any way.

[quote=“bitshit,post:19,topic:28237”]
It can be done, quite easily actually, but there’s a big difference between a texturing demo and a full-blown library that supports SVG rendering.