The grass shader we made for Crumble contribute in the unique aspect of the game. You can roll through it and the grass will bend giving an illusion of dynamism.
First of all, I want to show you what was my intents before doing a grass shader for Crumble:
I wanted the grass to be fully dynamic, so I actually made a model of grass and scripted a little interaction with the player. When the player gets near enough, the grass will scale down. What I really wanted to achieve with these grasses was to have a perfect blend with the terrain under. You can already understand the problem with this method in a 3D platformer, you would need a lot of grass placed manually on each prefab, it will take time, memory and process power.
So the only way to make this work and be performant, would be to use shader.
We know what we want for our grass, but how do we do it with shader, the first thing that came to mind was to use volume rendering. If you’re not familiar with it, here’s an explanation:
This is a very informative image taken from: http://www.xbdev.net/directx3dx/specialX/Fur/index.php
Which explains the method in more details.
This is what we want to emulate, volume for our grass without thousands vertex for each grass.
Let’s start with the mesh:
I use blender for modelling and texturing.
This look pretty simple to do, you need to make one layer and duplicate it (16 times in my case). The more layer you have, the more detailed it will be. I think for grass in a third person camera game, between 8 and 16 is ok. Try to keep the same distance between each plane to keep it consistent.
Now why do I have a black and white gradient on the right image?
I modified the vertex color of every plane, from black to white. Black being the base and white being the top, so that in my shader I can later use that data information to lerp the alpha of a single texture. Which means that the bottom will have no alpha at all, and the top will be mostly transparent.
I made a python script in Blender to assign vertex color for mesh incrementally (I’m a total noob in Python, so I have no idea what I’m doing):
Now onto unity, in shader:
The noise is a combination of a fur texture and a noise texture that helps creating a little variation. And we’re clipping instead of using pure alpha transparency to avoid render queue conflict and headaches.
This is what we get:
We have grass but now we need to make it move.
We simply use uv scrolling, since we used textures to simulate volume we can just scroll said textures, contrary to vertex deformation for geometric grass.
I only scroll the fur pattern of the shader because I want to keep the texture that makes the hole in the grass static.
The most fascinating part of the shader is the trail that leaves the player behind when he rolls.
I use a technique of render texture to accomplish this effect, an orthographic camera points downward following the player with a height offset. Then we have a particle effect emitted by the player:
These blue particles will be rendered by the orthographic camera, then be applied onto the grass shader, using global texture parameter:
And the result :
Taking into account the topology
So now things get serious, because we are projecting a 2D texture onto a 3D mesh texture, the shader ignores difference in height:
It seems like nothing, but this is a big deal, and shows the limitation of the technique. Stubborn as I am, I’ve solved this issue. I have to give you a warning though, it is not the best way to solve it, so if you want to have the same effect in your game, I suggest you stop at this step.
I created another orthographic camera on top of the player, same as the first. But instead of rendering the blue particles, I’m rendering the depth pass of the image, and applying it to the grass shader:
The result after Depth pass mask:
And that’s it, there’s a lot more to it than what I’ve explained here, but I’m sure I’ve covered enough of it for anyone to understand. For instance, we had a problem with how player shadow is handled. We have a projector for that purpose so you can imagine the problem with redrawing 2K vertex each time. We had to directly implement shadow projection in the shader of grass, we will surely have to rework it for the multiplayer part of the game.
Thanks for reading until the end, I know it was a popular request, so I really wanted to be thorough.
Don't forget to wishlist the game, if you want to help me that's the BEST WAY: