Effective Title: Procedural grass
Year Of Talk: 2021
Video Link: Procedural Grass in 'Ghost of Tsushima'
Originally attempted using common grass systems.
Trying to attach animations of grass to the system limited the ability to render.
The render distance of the grass was greatly hindered.
Outerras blog post about procedural grass was a large inspiration. Blog post found here: https://outerra.blogspot.com/2012/05/procedural-grass-rendering.html
The scene will calculate around 1 million and render around 800k of those grass blades.
Each grass is configurable by designers and calculates how it is affected by the wind in around 2.5 Milliseconds.
Set up to also react to the player's movement.
Compute Shader, Data pipeline, Vertex Shader, Pixel Shader, Miscellaneous
Compute Shader:
Tile system for the world information. From height values for terrain, materials needed, and type of grass. It is then subdivided even more
For each rendered tile the engine will run a single compute shader.
Each position/lane in the tile is turned into a new grass blade and a small amount of jitter is added to better simulate the randomness of the grass position.
All distance and frustum callings are done. Essentially if something is too far from the player to see or behind the player, it won't be rendered. This helps reduce the amount of things that are being rendered at once to help performance. Good idea to add some extra space to it seems a bit natural that you can always see grass.
At this point, the grass type and height information is acquired from the tiles and placed on the map. If no grass was chosen or the height was lower than 1 it will not be spawned.
When generating specific grass blades 16 floats of memory are used to store any information needed. The video doesn't fully clarify whether this is a 16-bit float system or 16 separate float values. The first system assumes you only have 16 bits (16 0’s or 1’s) to represent the different values which helps with storage but loses information. Having 16 individual float values will allow for more specific values as you have more values to work with allowing for an increase in accuracy but taking up more space in memory. It is likely individual float values as some of the values require more than one bit to allow for the randomness seen in the real world.
List of the different features used to determine how the grass looks like:
(All the 16 floats used)
3 floats for the position (X, Y, Z)
2 floats for the facing direction
Strength of wind on grass
Hash system to help with animation and other smaller details.
Grass type which designers can customize to add variety to different grass.
2 floats for which direction the grass is meant to face
The color of the grass depends on the clump it is attached to.
Height of grass
Width of grass
Tilt of grass
Bend of grass
Side curve of grass
Before the implementation of the clamp system, many areas with grass created a uniform look, unlike real-world grass that grows more chaotic as some areas may receive more sun, and water or have different levels of nutrients that can affect how the grass looks like.
They implemented a procedural Voronoi algorithm to help create the clamp system.
Voronoi algorithm is when one partitions a 2d plane into N subplanes where each subplane contains a singular reference/generating point inside the walls. The idea is all pixels found in a subplane have a shorter distance to the reference point than compared to other reference points on the plane.
This system is primarily used for creating terrain features as you may want to find find all the closest surrounding areas that are affected by a specific terrain feature.
The following website goes into details of how it looks as well as a code sample to run the algorithm. http://pcg.wikidot.com/pcg-algorithm:voronoi-diagram#:~:text=Voronoi%20Diagrams%20is%20an%20algorithm,the%20pixel%20for%20that%20point.
It would be interesting to create Risk maps or when making a political map for a strategy game where each new game the players need to contend with a newly generated map. Rouge-like + political/4X game is an idea that might be an interesting game. Might be the next game I test once I finish the other couple of games I have in the works.
For Ghost of Tsushima, they acquire 9 points on the plane and then implement a hash function allowing for some variation. Then the point is attached to the closet reference point.
After this each subplane (Clump of grass) can be given more information by designers to allow variation in height, size, how spread out it is from the clamp, and other features.
Data pipeline:
The following graph shows how the game will compute the grass. As a note to the image, compute 1 tends to be the one with the most amount of information to calculate while compute 2 takes only a few moments. It is set up so that compute 2 does not start until compute 1 is complete.
The compute order goes from Compute 1 → Compute 2 → Vertex → Pixel.
The tiles are set up in a cascading system with one of the two data buffers starting the compute values for the next tile once the other instance data buffer has finished its compute values and is implementing the vertex values. This allows for the work to be better spread out and not overwork the GPU.
Vertex Shader:
The high level of detail (LOD) grass contains around 15 vertices while the lower LOD contains only 7 vertices.
All the vertices are done by an indexed draw call instead of a vertex stream. Only need index and instance ID.
I dont fully understand how index draw calls work compared to normal draw calls. From what I can gather from reading online, what normally happens is each vertex is sent to the GPU or graphics API to render the information onto the screen which contains a lot of information for the computer to use on what it is meant to look like and other important information. But by indexing the vertices you allow for the important information to be streamlined and can be processed faster. This seems like you are just taking out information you dont need. I can see this making stuff faster, but how do you know you're not taking out too much information for the graphics? If someone reading this can help me understand the difference and how this works then please contact me.
There was also an implementation to allow for easier blending between the high and low LOD for the grass. Can cause some problems if the grass is at an angle.
Cubic Bezier curve
This math function helps determine how fast or slow an item or object will transition between two points over time.
Basically, a math function that allows you to determine how fast an item will progress over time from point A to B. You can have it go very fast at the start and end but very slow during the middle or vice versa. Any other combination is possible.
I recommend visiting the first website link below if you want to visually see what it looks like and how changing certain values affects it. The second link leads to a deeper math explanation and more examples.
Visual representation: https://cubic-bezier.com/#.99,.92,.31,1.01
In-depth Explanation: https://blog.maximeheckel.com/posts/cubic-bezier-from-math-to-motion/
Easy to calculate position and derivative. This curve also helps determine how the animation should react as time goes on and is being affected by wind. This also helps with determining the curve of the grass.
The wind works in a unified system that can be calculated in both the CPU and GPU. It is a 2d purlin noise calculation that moves in the direction that the wind is moving. This combines with the 2d wind vector as inputs for the wind to help react appropriately. Some grass and particles receive an extra purlin noise factor to help increase realism.
Bill Rockenbecks's talk has more information about the wind system.
A hash value is calculated for each blade as a way to help differentiate the grass movement.
The normal values rounded a bit to help it implement a more rounded look to the grass blades.
The angle of the grass is slightly moved to face the player as it helps make the fields look even more full without the need for more grass blades.
Problems in the mid to long distance with a speculative view as normals can change drastically depending on the distance. Which added extra glitter to the grass which was not intended. A common normal was place and as the grass is farther away from the camera the closer it got to said value.
Pixel Shader:
When rendering it outputs the data to the graphics buffer.
Gloss texture has a 1D texture that goes through the middle meant to show the vein of the grass blade.
Diffuse contains two separate textures. The first one is the vein in the middle of the grass helps with width. Meanwhile, the second texture contains color.
Translucency has a constant value throughout the grass blade depending on the thickness of the grass. Ambient Occlusion (AO) texture has the same feature.
AO texture instead of a Screen Space Ambient Occlusion (SSAO)?
Because of how random the grass blades can animate and move around in the same spot can make it even more difficult as the wind can easily change between a few frames. If the player also moved recently over the grass it will have an even larger change in the SSAO which can make the computation very costly.
At times designers and artists implement new art assets throughout the grass to allow for the grass to look more unique and different from other similar tiles
3X3 tiles are normally rendered at one time
A big struggle was spawning grass at very large distances. Specifically when high up when the player can see large areas of the map.
The final solution was to have assets from artists replace certain areas at long distances as those had an easier time being rendered as a single item instead of many smaller items.
Some information from the GPU render is copied onto the CPU to allow it to calculate what areas the player can sneak around in the grass.
Each time grass has stealth data which determines if the player can use it for stealth. If this is true then the top fus is added so the player understands what areas are accessible to stealth in.
Shadow is cast normally using the underlying terrain as it can be less expensive than using other means.
Also used screen space shadows to better implement the shadow system though this was normally limited to short grass. Though that is the main type of grass seen in-game.
Some improvements:
They were restricted to grass only appearing on terrain so if artists wanted to implement grass in a new manner they may be limited.
The game currently grab the assets and sample part of it as well as the terrain it connects to help the transition between grass and non grass area.
In the future, they plan on adding a more seamless transition between the grass terrain and assets.
In the future, they wish to add more grass types and assets they plan to work with the procedural generation of the grass to allow for an easier time planning and rendering the scenes.
Another problem is when transitioning between certain distances for the tile it greatly reduces the grass count. (Every 4 grass blades turn to 1). Hopefully, in the future improve the distance where they need to start reducing the amount of grass blades per tile so the player can see more grass for a longer time.
Comments