Advice for path of least resistance for rendering a lot of text/lyrics on-screen


#1

Dear Embers,

I’m implementing a new feature and I could use a bit of sage advice from someone more experience with graphics rendering.

The goal is to render the entire set of lyrics of a song using 3D text on-screen with various shaders (often the same, but not always) applied to every line of text. Each line of text should have its own translation/rotation. The resources for rendering are to be made available (and released) real-time for various songs, not specified in advance.

I’ve come up with two approaches in mind, and a set of pro’s and con’s to each, and I’m hoping to gather a bit more wisdom regarding each, and possibly even a strong suggestion to which might be ideal in the long term.

Approach One:

Once the song to use has been decided, create one ci::gl::BatchRef per each lyric line, put these in a big vector and render with each shader and translation/rotation as needed.

Pros:

  • Implementation-wise fairly straight-forward - one ci::gl::BatchRef per lyric line
  • Rendering each line with its own translation/rotation/shader(s) is super easy

Cons:

  • Graphics meshes indirectly tied to the songs which isn’t as pretty as approach two
  • Each song requires a fair amount of resource acquisition upon load
  • Possible high use of Vao, Vbo (one per batch)

Approach Two:

Create a sort of text-character atlas, one ci::gl::BatchRef for each symbol needed. Use instanced rendering and position each letter where needed in each lyric line using said letter.

Pros:

  • Text meshes are created one single time (one for each letter)
  • Likely the least resource intensive approach

Cons:

  • Far more complex rendering code - must position each instance of each letter in each lyric as needed
  • Not sure if attaching different shaders to different letters is even possible using instanced rendering

Any thoughts on either approach is much appreciated!

Thanks in advance,

Gazoo


#2

Hi,

when it comes to performance, always try to do as much work as possible in one draw call. This means that your second approach is probably better than your first one. You already pointed out the cons: it requires one draw call per glyph and you’d have to do all text shaping (the art of properly positioning glyphs) yourself.

Regarding different shading per glyph: if you can come up with a set of parameters that together control the look and feel, you might be able to use a single shader for all glyphs. Just fill a buffer with per-glyph parameters and access them from the shader.

Otherwise, you won’t be able to apply a different shader per glyph, unless you use a single draw call per glyph.

In theory, it would be awesome if you could also dynamically choose a different mesh per instance, but as far as I know this is not possible or practical.


#3

Hello,

Thanks for taking the time - your feedback is invaluable as usual @paul.houx!

True - a sort of multi-effect shader would be able to make allow each instance to be rendered in various styles. At the same time - the more I think about it - the more it seems the second optimized approach requires a greater clarity when implementing the visual effects and at this stage I’m not exactly sure what they’d look like. I’m honestly just playing around with things a bit.

I’m not quite sure I follow you when you state ‘dynamically choose a different mesh per instance’. Isn’t the whole concept of instancing that the mesh is 100% the same? Perhaps you’re contemplating a series of fixed meshes to choose between…?

Cheers,
Gazoo


#4

Yeah, if you could somehow render multiple different meshes in a single draw call, it would help reduce the total number of draw calls in your case.

But the only way this might be achieved, is by accessing a buffer in a geometry shader by passing a per-instance ID or something. In the geometry shader, you’d then read the vertices in the buffer and generate the triangles for the mesh. But I’m almost 100% sure this will perform terribly. You’d have to generate lots of vertices and primitives per input vertex. If it’s at all possible.

So I guess you’re stuck with one draw call per glyph.

-Paul


#5

An interesting idea for sure.

I’d also need to get a much better grasp of the geometry shader.

For now, despite the negative performance impact, I’ll start with approach one I think as that will get me moving faster despite poorer real-time performance. I’ve been thinking about whether or not I’ll also run into an issue of running out of VAO’s and VBO’s, but I figure a stop gap measure would be to render an entire songs lyrics into a single VAO and VBO and then somehow attach parameters detailing where a line of text is to be rendered and if it’s to be visualized at all.

Gazoo


#6

Actually there is an AZDO technique for rendering multiple different meshes in a single draw call, called Multi-Draw Indirect and implemented as an extension ( GL_ARB_multi_draw_indirect ) that could potentially be useful in this case I believe, although I can imagine setting up the buffers for this specific use case to be a bit challenging.

I have played with this in the past in order to render thousands of different tetrahedra meshes on a single draw call and it was quite rewarding I remember - If there is an interest on how this could be setup with Cinder, the code can be found here .

Cheers,
Petros


#7

That’s some excellent advice, Petros. I completely forgot about that.