Outline a 2D Shape


I’ve been working with SVGs in a 2D project recently, and have been wanting to add an outline to some of the geometry I’ve imported. Been reading a lot about the different ways of adding an outline in OpenGL or with shaders. One technique uses a distance field texture to determine any single fragment’s distance from an alpha fragment in a shader to make it a different color. It seems as though you need to generate this distance field ahead of time however, and combine it with another color texture, not the vertex based geometry I’m drawing with my SVG data. Another technique I’ve seen a few times is the simpler idea of just drawing the geometry once, in the color of the border, then scaling it down from the center by some amount that equals the size of the desired outline, then drawing it again in the color of the fill. In theory this sounded perfect, and I’ve definitely used this technique when drawing simple shapes before, but I’m running into issues with my SVG imported geometry. I have saved the imported SVG vertex info to a BatchRef instance by triangulating the DocRef->getShapeAbsolute() object. Now this batch object has all of the SVG shapes in a single Vbo, so scaling down from center doesn’t really work. Hopefully these images help explain:

Draw a red rectangle, scale down a little and draw a black rectangle to get an outline result - perfect. Draw a red SVG document of some text, scale down a little and draw again in black to (ideally) get an outline result - not so perfect:

You can see what happens is that the Vbo for the batch object is made up of several disconnected shapes, so scaling and redrawing doesn’t work that way.

Do I need to break down my SVG document into separate shapes? Is there another ‘magic’ border shader that I haven’t run into yet to accomplish this? FWIW - I can call gl::draw(*mSvgDoc) and it will only draw the outline of my document, however in a very thin line that I can’t seem to figure out how to make any thicker. (Have seen notes about GL_LINE_WIDTH being deprecated, not sure if that’s it either).

Also, I’m drawing these shapes many times in a single frame, so ideally the solution isn’t just gl::draw(), gl::scale(), gl::drawSolid(), but perhaps that’s the only way?.

Any help is always appreciated, you guys are awesome!

  • Max.

Hi Max,

If you want to draw thicker lines one of the ways to accomplish this is using Paul’s GeometryShader example.


1 Like


you could still try to use gl::lineWidth(), some drivers still support it. My NVIDIA GPU, for example, but not the Intel GPU in my laptop.

I would also investigate if SVG can create outlines for you.


This sounds like a “Dilation” operation. Depending on your requirements, maybe you could implement it with OpenCV?


Thanks guys! I think the easiest thing for my project is to just have another BatchRef that is the border and draw that on top of the fill. Going to look into some of these other options for some ideas going forward.

  • Max.

You can combine the two meshes before you pass them to the Batch constructor using SourceMods::operator &.

gl::Batch::create ( ( geom::Teapot() & geom::Icosphere() ), gl::getStockShader( gl::ShaderDef().color() ) );

Where the teapot might be your outline mesh and the icosphere the fill. Obviously this implies that the two would never need to be drawn separately.

1 Like

That’s awesome, thanks for the tip! Only thing is that they need to be different colors, how would I go about doing that? Currently all the SVGs I import are just black, I’m using gl::ShaderDef().color() with my batch and setting the color with gl::color() before drawing the batch. I assume if I combine them, they will both be affected by that color when drawn. The outline could actually always be the same color if that is possible, but I do need to be able to change the fill color.

Thanks again!

There’s so many different ways to go about this, and this may not be the most efficient, but since your outline only needs to be black, you can just multiply all colors by the desired fill color and the black ones will stay black, and the rest will modulate with their own colors, so anything white will become 100% the active color.

In the case of your svg, after you load it into a TriMesh but before you create the batch, you’ll need to set all the vertices that need to change color to white (or if editing the source SVG is an option, just make them white in the original vector art)