Making Grass Fun(ish)
Videogames are interactive - whether it's a character reacting to user input or an epic RPG ending in wildly different ways based on player choices, it's all consequences of interaction. But while implementing fancy dialogue trees, adaptive story design and other interactive goodness, it's important not to forget that it's often the smallest interactive elements that makes the biggest difference - they can make or break our subconscious perception of the game's feel and overall quality.
Today's example - grass. When you're standing on a hilltop overlooking the realm in your favorite game, there's nothing quite as good for immersion as seeing all those tall blades of grass slowly swaying in the wind. At the same time, there's also nothing quite as bad for that same immersion as crouching through those tall blades of grass and literally nothing happens - making you feel more like a ghost than the grand adventurer you're supposed to be. And I get it - reactive foliage can be quite demanding, as I discovered when I implemented it. But I wanted it in Ronn For Your Life - so here are my challenges and solutions in the implementation.
First, let's take a look at what it looked like before:
While overhauling everything, it was clear that the grass pictured above was just one of many things that had to go. I wanted something that looked more natural, gave me greater control of programmatical influence (like changing wind forces), was lighter in terms of performance, and most importantly had the ability to be interacted with by external forces beyond wind - like a character walking through it, bending to its movements.
Vertex Buffers & Shading To The Rescue
The old grass patches were just instances of an object that had a random sprite with a random animation speed. Not only was this costly in terms of performance; it also didn't look very good, and it gave me very little control. Instead, using vertex buffers and a shader to distort whatever grass image I passed in allowed interactivity by bending those vertices to external forces. Performance wise, I initially planned to just have one instance that would fill the ground with foliage - this would be extremely cheap. But it would give me less control, because the shader can only be fed with coordinates of one external force at a time - this would mean that the player would be able to bend the grass, but the NPC right next to him would not. This would be even worse for immersion - all entities inhabiting the world must adhere to the same basic rules.
So instead I made each patch of grass 32 pixels wide, filled with single randomized grass blade images (each individually distorted by the shader) - anywhere between 8 and 32 - with 32 being the entire width of the grass patch filled. Any "ground" instance will then automatically calculate how many patches it needs to generate based on its own width, which handles the foliage generation for me.
Looks & Performance: Finding The Sweet Spot
Filling the ground with foliage this way requires a bigger amount of instances (instead of just one), but in return, each grass patch can react to whatever is actively treading through it. The width of 32 pixels is just an arbitrary sweet spot between performance, looks and interactivity; the more instances active at one time, the worse for performance - but decreasing the number of instances would mean widening each patch of grass, and if two characters are walking through that same patch, only one of them would actually be bending the blades in it.
So a patch should be big enough to take up enough space to lower the number of instances needed to fill the ground - while also small enough that the width of the characters will cover it, removing any need for more than one character affecting the blades simultaneously.
A medium sized level could easily have more than 500 foliage instances which, even with this fairly cheap method, would be way too much. So I used a little trick where every patch of grass that is outside the camera's view will automatically be deactivated, meaning it will no longer be rendered or processed in any way. The camera will then automatically reactivate any deactivated instances within its own bounds. In a small test where I had 500 foliage active, this trick actually turned those 500 into a maximum of around 40 at any one time, and sometimes down to as little as 15 in a level filled with foliage. It also bumped the average FPS from 80 to 1000 - not an insignificant increase. Obviously the maximum number of active foliage instances will vary slightly depending on the user's resolution - a lower resolution means fewer, and a higher resolution means more. But in the grand scheme of things, the difference here should be negligible - especially for players already gaming at a resolution high enough for it to matter.
Alright, enough talk - here's what the result looks like (disclaimer: all graphics you see are placeholder and for demonstration purposes!):
If I removed the interactivity, I could run all foliage with one instance, making it much cheaper - but the cost of "cool factor" would be too high. I will need to do some further testing on old craptops to ensure that the game will run on just about any potato, but it shouldn't be a problem.
- There is a performance cost, but it's worth it because the interactivity greatly enhances the visual effect and overall immersion
- Even if performance should become a concern, I could make an option for toggling interactive foliage and have the non-interactive version be 1-instance based
- I can still gain significant performance boost from optimizing the method which which I activate/deactivate foliage - so there is still performance to be gained
What do you think? Are the improvements worth the work? Do you prefer the new or the old look? Leave your feedback in the comment section!