30 Jul 2017

This stackexchange question was about using shaders to make hexagon boundaries “noisy”. Having just worked on fragment shaders for polygon boundaries, I adapted that code for this demo, and added some fun sliders.





The first key idea is that it’s easier to make interesting boundaries when you move them into the center of what you’re drawing. Instead of drawing the tiles directly, you draw the “dual” of the tile. This technique is called “corner tiles” (here and here and here) and the same thing works for hexagons. The dual of a hexagon is a triangle, so we would draw these triangles instead of the hexagons:

Inside each of those triangles we want the fragment shader to draw the hexagons. We can do that with *barycentric coordinates*. Put (1,0,0), (0,1,0), and (0,0,1) at each vertex of the triangle. Inside the triangle, those coordinates will be interpolated. The fragment shader will receive (a,b,c) and can look to see which value is largest — that will tell us which of the three hexagons should be drawn at this point. By adding noise or bias to the barycentric coordinates, we get the effects in the demo above.

For this demo I’m using the GLSL noise functions from Morgan McGuire, BSD licensed, found in this shadertoy.

Implementation note: I ran into three browser issues with this demo:

  1. Thanks to an old version of IE, a dom element with id="foo" becomes a global variable named foo (see this explanation). Chrome and Firefox let me get away with this but Safari did not. I fixed this by renaming the id.
  2. In some browsers I was seeing occasional blank images. I was redrawing every time you moved the slider but if I move the slider quickly it might trigger two redraws in the same frame. I fixed this by using requestAnimationFrame and making sure I had only one of them active at a time.
  3. In some WebGL implementations there wasn’t enough precision for the shader to return the correct results. I fixed this by using highp precision, but I think the better way would probably be to rewrite my max(a,b) == a style constructs to use a >= b instead.