Home

Awesome

Unity Geometry Grass Shader

A Geometry shader written for Unity's build-in Render Pipeline

Geometry Grass Shader

What was the Problem?

There are many games that render tons of grass without the GPU breaking as much as a sweat (Genshin Impact, Breath of the Wild, etc.). While researching the topic, I came across the solutions for rendering large amounts of objects without straining the CPU by off-loading most / all of the work to the GPU. One such solution was to use Unity's Graphics.DrawMeshInstanced and Graphics.DrawMeshInstancedIndirect, which renders provided Meshes with a Material that supports GPU Instancing. The Shader on the Material would be used as a way to render the Mesh on the right position, by getting location data through a ComputeBuffer.

Using this method, Colin Leung created a great example of how to combine the Graphics.DrawMeshInstancedIndirect with a generated grass Mesh and a complicated Material to show a large field of grass (10 million instances) with great performance, even supporting bending of the grass when an object runs through the generated grass.

This would fix the problem of generating a great amount of grass while utilizing GPU Instancing, however this solution would not work when taking into account the height of the terrain. In the example the grass is generated at a certain height, which is not variable. You could add support for a height map to tell the grass Material at which height it should be offset, but this would not work when there are two platforms above each other, which both need grass rendered on them (technically it could work, though it would require quite a workaround or several GPU Instancers to make it work).

What was the Solution?

Geometry shaders were introduced in Direct3D 10 and OpgenGL 3.2, which is a type of shader that can generate points, lines and triangles and are generated after the vertex shader and before the vertices are processed for the fragment shader. The geometry shader can use the vertex as input, so it contains the world position of said vertex, removing the need to create an offset that was needed with GPU Instancing.

The tutorial from Erik Roystan Ross is amazing at explaining exactly how to utilize the a geometry shader to generate grass on a model in his tutorial "Grass Shader". In the tutorial they also combine the vertex shader with a custom tessellation script, which was adapted from the Catlike Coding article on Advanced Rendering. Both of these articles were an extremely interesting read. I used Erik Roystan Ross' shader code to generate the grass in a geometry shader and extended it to support the features that I wanted to have in my shader.

What does the shader feature?

The shader features everything contained in Erik Roystan Ross' tutorial on how to write a grass geometry shader and adds extra features on top of it to generate grass similar to previously said games.

Roystan's Features

Added Features

How to use the shader?

You can clone the project and use the CustomTessellation.cginc and GeometryGrassShader.shader, which is the barebone to create use the geometry shaders.

The repository is an example of how to use the shader in combination with all the properties provided. In the scene Scenes/SampleScene there's the example shown in the screenshot on the top of this Readme.

<p align="center"> <img src="https://i.imgur.com/qvWDxCs.png"> </p>

Future Features?

If I've got time I would love to update the shader to support more features. Though this would require time, since I'm completely new to writing shaders, let alone geometry shaders. I couldn't write this shader without the amazing tutorials and information that is already written for me (like the articles from Catlike Coding and Roystan).

Displacement

With some tinkering I've managed to add the ability to displace the grass based on an object's position using a RenderTexture. Attached to an object is a displacement texture that has R(ed) and B(lue) values that correspond to the direction the grass should bend towards.

<p align="center"> <img src="https://i.imgur.com/ZGjPZuM.gif"> </p>

The texture used is in the image below and is part of the object, moving with it in world space.

<p align="center"> <img src="https://i.imgur.com/ZzwSHiM.png"> </p>

Every object that wants to support displacement needs a displacement texture and a DisplacementObject script on them. This script will influence the transparency of the displacement texture to have an effect on the strength of it's influence on the grass' displacement. This is showcased in the example scene.