Note that this is a generic map renderer. Given two grayscale images with elevation (heightmap) and moisture, it will use SVG filters to turn them into what you see above. For the demo I use SVG’s perlin noise to generate both the elevation and moisture images.
Maybe I should’ve used GL shaders, but I wanted to see what SVG could do with filters:
- SVG feTurbulence gives us perlin noise. Let’s use red channel for elevation and green channel for moisture.
- SVG feColorMatrix gives me a way to convert (elevation,moisture) to browns and greens
- SVG feComponentTransfer lets me do thresholding. When elevation < some_value, alpha = 1; else alpha = 0. This will be the water layer.
- SVG convolve filter gives me an emboss effect
The matrix trick works by defining a color vector for elevation (0 elevation to 1 elevation) and another color vector for moisture (0 to 1), and then adding them for the actual elevation and moisture. Most color tables aren’t linearly separable like this. I picked some colors for the four corners of the table, and then I tried to come up with some vectors that approximated those colors. They’re pretty close, so I got lucky!
Color square from mapgen2 E=0 M=0 210 185 139 subtropical desert E=1 M=0 201 210 155 temperate desert E=0 M=1 68 136 85 tropical rain forest E=1 M=1 153 170 119 taiga Vectors on the four sides of the square At M=0 E vector is 210 -9, 185 +25, 139 +16 At M=1 E vector is 68 +85, 136 +34, 85 +34 At E=0 M vector is 210 -142, 185 -49, 139 -54 At E=1 M vector is 201 -48, 210 -40, 155 -36 I can approximate this with Base is 210 185 139 E vector is +40 +30 +25 M vector is -100 -45 -45 Reconstructed values at the four corners of the square: E=0 M=0 210 185 139 -- exact match, by definitions E=1 M=0 250 215 164 -- too red? E=0 M=1 110 140 94 -- too red? E=1 M=1 150 170 119 -- just right After plugging these initial values in, the colors seemed too boring, so I tweaked the values in the matrix until I got something I liked.
In GL, we could do this with a texture lookup instead. It gives us a lot more flexibility. You’d embed the Whittaker Diagram colors into a texture and then use u = elevation, v = moisture as the lookup coordinates. You can directly include water in the texture instead of the separate image I used for the SVG diagram.
If this kind of thing fascinates you, check out Toby Schachman’s Shadershop and this libnoise tutorial. In 2021 I found this amazing demo of svg filters for map generation.