I’ve been working on loading and saving height maps for terrain lately. My first attempt at this just used a grayscale image and the engine read the [r, g, b] values from each pixel and placed the corresponding terrain vertex at the appropriate height. This works fine but it’s not a very efficient use of the image data. Since the image is grayscale each pixel’s [r, g, b] value is the same number repeated three times.
I don’t care so much about the inefficiency but the lack of resolution was annoying. What I mean is that in a grayscale image each pixel is somewhere between [0, 0, 0] (ignoring alpha values) and [255, 255, 255]. That means the world’s terrain can only have 256 distinct height values. This can be scaled arbitrarily so I might have each step set at 0.1 meters, but then the highest point in the game could only ever be 25 meters. Actually, it would be half of that since the terrain has to be able to account for distances below sea level as well. Or I might have each step be 2 meters so that I could have towering peaks of 256 meters and abyssal depths of 256 meters, but the terrain itself would jump up and down by increments as high as player characters are tall, so neither of these is ideal.
Since this is quite insufficient for a world that’s at all interesting I have been looking at other options. My first though was to just add r + g + b to get a range of 768. Divided in half this gives me a max height of 384 meters. I could live with this and just accept that Lyridia is a fairly flat place but my terrain step distance would still be 1 meter, and I just don’t think that would look very good. Again, I could scale this down but then my max height goes down and the world gets pretty flat and boring again. Plus, with this method a pixel with color [255, 0, 0] represents the same height as another with [128, 127, 0] or another with [0, 255, 0] or another with [2, 0, 253]. With so many colors representing the same ultimate value I knew there had to be a better way to store more resolution in a standard image.
I kept coming back to the number 16.7 million in my head. That’s the number of possible color combinations for a 24 bit monitor. It’s 256 x 256 x 256. With that in mind, I figured there must be a way to get 16.7 million discreet levels in a world terrain and this is (I think) how you do it.
For any pixel, the [r] value has the largest effect. I thought it would be fun to have a maximum terrain delta of 65,536 meters, so I multiply each [r] value by 256 to get r. Then each [g] value is left unmodified as g, then each [b] value is divided by 255 to get b. Now just add r + g + b to get your terrain height. No need to scale anything as this gives you a massive delta by altering the [r] values but extremely fine gradations by altering the [b] value. A terrain height represented by [0, 0, 1] is just 0.003922 meters high! That’s less than a quarter of an inch. Yet [255, 255, 255] is over 40 miles high!
This may all be incredibly obvious to anyone who has done this type of work before but that’s definitely not me so it was fun and challenging to figure out how to make this work. The long and the short of it is that I can store height maps for Lyridia as standard .png files with more resolution than I’ll ever need and I’m pretty excited about that.