This month is supposed to be about adding vegetation to my terrain. It started well, then went badly, and is now going well again. So that’s OK, I guess.
The good start was making a Poisson distribution function for grass. For a convincing 3D game landscape, your digital grass has to be spread out randomly but smoothly, and smoothly random is something computers are very bad at. If you just used an ordinary rand() function, it would end up an unrealistic mess of statistical gaps and clusters. Poisson distribution generates random points that are smoothly spread out, and I couldn’t have been more pleased with myself when I got that working.
Remind me again what pride comes before?
The next stage was to mould my 2D plane of smoothly distributed points over my 3D terrain. This is trickier than it sounds, because basically for each 2D point – and there are thousands and thousands of them – you have to sample all the vertical space they could occupy to find the surface of the terrain at that position. And you have to do it fast, because your player is waiting. Doing it fast means using shaders, and shaders can be a bugger to code.
I created a reference texture of my terrain’s height – a sort of snapshot from above – so that I could read the height values off of that as needed. And for some reason I could not read that damn texture back – I just got garbage or zeroes. I wasted the best part of week trying to find out why, because once I’d wasted a certain amount of time doing that I felt I had to keep going or I would have wasted even more time. The kicker is that I still don’t know: eventually I had to cut my loses and give up. Return on time invested: zero.
The good news is that I found a different solution, and a better one. I’m rather proud of it. I feed the points into a shader that samples the terrain at two different heights, and if the surface lies somewhere between them it calculates where by interpolation. The clever bit is that you do this about a dozen times, raising the sampling height each time and feeding the data it spits out back in as the next round of input. Your 2D points rise to the surface like bubbles in champagne.
Finally I could get onto the instancing, which is something I’ve been itching to try.
Instancing is where your graphics card saves you a massive amount of bandwidth by letting you draw the same model over and over again for almost no extra cost. If you ever wondered why games once couldn’t show you things like vast asteroid belts in Elite: Dangerous, and now they can, it’s because of instancing.
Making instanced grass can come later. After all my hard work mapping those evenly spread points to my terrain, I wanted to see how they looked. Answer: amazing. I knew instancing let you draw lots of the same thing at once really fast, but I was still amazed by just how many of that thing it would draw, without my badly unoptimised code even breaking a sweat.
If you ever wondered what seven hundred and thirty one thousand four hundred and sixty coloured cubes looks like, it looks like this:
I had great fun running around what was suddenly a very busy landscape.
And then I had an idea. What would it look like if I turned off the landscape, and it was just cubes?
Answer: Even. More. Amazing: