Grass is rendered as a set of 2D textures (blades), each representing a small grass tuft. Grass diversity is achieved by the probabilistic factor that influences the grass blades height, offset, color, etc., which grants convincing illusion of natural variation with fine details, while not monopolizing the GPU. Grass can be animated, which makes it realistically sway in the wind.
To add grass to the world, perform the following:
- On the Menu bar, click Create -> Grass -> Base.
- Place the object somewhere in the world.
The default grass field would appear with the grass_base material assigned.
Rendering of grass mostly depends on two textures:
- Diffuse texture defines the grass shape and color.
- Mask texture determines grass mapping and density distribution on the terrain.
For convenience, diffuse texture is subdivided into chunks. Each chunk is rendered depending on the used mask values. To utilize maximum variations of grass species, chunks are placed in the texture as follows:
- The horizontal row can have up to 4 chunks. Each chunk corresponds to the red, green, blue, and alpha channels of the mask.
- The number of chunks in the vertical column can be from 1 to 4. Chunks in the column represent variations of grass for the corresponding mask channel. Usually, each channel corresponds to the particular species of grass (thiner, thicker, tangled, etc.). If there are several grass clusters in a vertical column, they are randomly spread across the masked area.
For the texture to be split into the correct number of chunks, the number of chunks that you've used per column should be specified in the Num textures field. For example, for the diffuse texture below, Num textures = 4 should be specified.
||Set in the Textures tab of the grass_base material.|
|Mask||RGBA8: each channel controls distribution of the chunks in the vertical column of the grass diffuse texture.
In case R8, RG8, or RGB8 texture is used as a mask, the diffuse texture must have only one, two, or three columns. So, if your diffuse texture contains, for example, only 1 vertical column, the R8 texture should be used.
0 mask channel values (no color) create areas where grass should not grow. Non-zero channel values create a grassy area: the higher the value, the denser grass grows.
If the mask texture is not set, the default white texture is used. It doesn't have a predefined amount of channels: the type of this texture depends on the value set in the Num textures field. So, if Num textures = 2, the default mask texture will be RG8.
|Set in the ObjectGrass -> Node tab -> Grass section - > Mask Image field.|
For example, we have the following diffuse and mask textures:
RBG and A channels of the mask
We see that in the first column, chunks of the green shades correspond to the red channel of the mask, which is 9 red rhombuses; in the second column, chunks of the real grass variation correspond to the green channel of the mask, which is the square green mask frame; in the third column, chunks of the red shades correspond to the blue channel of the mask, which is the blue rhombuses frames; and in the fourth column, chunks of the yellow shades correspond to the alpha channel of the mask, which is the rest black areas (note that these parts won't re rendered, as there no alpha color data).
The result will be as follows:
To randomize appearance of grass blades, two types of values are used:
- Mean value (set in the main field of the parameter: Min/Max Bend, Min/Max Height, Aspect, Offset) defines the average value of distribution. Four fields correspond to the red, green, blue, and alpha channels of the diffuse texture correspondingly. Using this value, the designer can set milestones to control the parameter.
- Spread value defines the range for possible variation of the parameter. Four fields correspond to the red, green, blue, and alpha channels of the diffuse texture correspondingly. The higher the value, the more diverse the final result is. The spread value is optional: if set to 0, it does not affect the simulation process. In such a case, only the mean value is uniformly used.
The probability of appearance of the grass chunk belonging to a specific mask channel (horizontal row) depends on the Probability parameter. The variations of the specie (vertical column) are rendered randomly.
Let's check several variations of the Probability parameter for the following diffuse texture:
The default RGBA8 white texture is used as a mask (as there are 4 chunks in the diffuse texture, we set Num textures to 4). The mask value is white = 1, therefore, the whole grass area is filled.
In the pictures below, different variations of the Probability parameter are shown:
Probability = 1 1 1 1
Probability = 0.1 0 0 1
Probability = 0 1 0 1
Probability = 0.1 0.3 0.7 1
Min and Max Bend#
Bending means sloping of a grass quad forward or backward at a certain angle.
Min Bend is the mean grass quad sloping value in areas with the lowest density according to the mask.
Max Bend is the mean grass quad sloping value in areas with the highest density according to the mask.
Both Min and Max Bend and their spread values are set for each diffuse texture column separately.
Min and Max Height#
Min Height is the mean grass height value in areas with the lowest density according to the mask.
Max Height is the mean grass height value in areas with the highest density according to the mask.
Both Min and Max Height and their spread values are set for each diffuse texture column separately.
Let's compare the following examples:
|If we set the Min Height value equal to 1 with the Spread value of 0.3, the height interval for the grass in the areas with the lowest density is from 0.7 (i.e., 1 - 0.3) to 1.3 (i.e., 1 + 0.3), meaning that each grass quad has its own height selected randomly from this interval.||With the Min Height value equal to 1.2 and the Spread value of 1, the height interval for the grass in the areas with the lowest density is from 0.2 (i.e., 1.2 - 1) to 2.2 (i.e., 1.2 + 1), meaning that each grass quad has its own height selected randomly from this interval.|
Min height = 1; Spread = 0.3
Min height = 1.2; Spread = 1
It works the same way for Max Height.
|For the Max Height value equal to 3 and the Spread value equal to 0.3, the height interval for the grass in the areas with the highest density is from 2.7 (i.e., 3 - 0.3) to 3.3 (i.e., 3 + 0.3), meaning that each grass quad has its own height selected randomly from this interval.||For the Max Height value equal to 3 and the Spread value equal to 1, the height interval for the grass in the areas with the highest density is from 2 (i.e., 3 - 1) to 4 (i.e., 3 + 1), meaning that each grass quad has its own height selected randomly from this interval.|
Max height = 3; Spread = 0.3
Max height = 3; Spread = 1
To render the intended grass quad, in addition to the height, it should also have the width set. To preserve the proportions of the quad, width is not specified directly, but rather using the aspect parameter. So the final width is calculated by multiplying Max height by Aspect.
Aspect is set for each diffuse texture column separately. Like the Height parameter, Aspect can be randomly varied if the Spread value is set in addition to the mean value. For example, setting spread to 0 yields the following result:
- If the mean value is set to 1, grass is rendered in square proportions, with the height equal to the width.
- If the mean value is set to 2, the width of the grass quad is twice its height.
Aspect = 1; Spread = 0
Aspect = 2; Spread = 0
Sometimes to avoid visual artifacts, it is necessary to slightly raise grass quads above the ground. This parameter specifies the offset in units up the surface normal, thus accurately repeating its relief. It is set by the mean value (only positive) and additional spread value (positive or negative), if variation is required.
Rotation of grass quads adds more diversity. This parameter specifies the rotation of grass quads in degrees in random directions . It is set by the mean value (only positive) and additional spread value (positive or negative), if variation is required. Values are set for each diffuse texture column separately.
On one single field, it may be necessary to simulate grass that had grown in good and bad conditions, shooting young grass blades and withered straw. The grass diffuse texture can have 4 x n slots, each containing a separate grass cluster, plant, or flower. The number of horizontal rows in the texture can be from 1 to 4. However, the number of vertical columns may vary depending on the way the grass distribution is specified:
- If the image mask is used to specify grass density distribution, the number of diffuse texture columns can be less than 4.The number of diffuse texture columns in this case is determined by the number of channels in the mask texture (e.g., diffuse texture must have 1 column if an R8 mask is used).
- If a mesh mask is used to specify grass distribution, the number of diffuse texture columns must be equal to 4.
- If both masks are used (the image mask sets grass density distribution and the mesh mask is used to exclude some areas, such as roads, the number of diffuse texture columns also must be equal to 4.
The Num textures parameter sets a number of horizontal rows with grass clusters, plants, or flowers available in the diffuse texture, and this value cannot exceed 4. After specifying the number, each grass cluster is rendered as a separate quad on the field.
4 Texture Slots; Num textures = 1
8 Texture Slots; Num textures = 2
To add more diversity to grass and avoid total uniformity of all blades on the field (especially required, if grass is represented as billboards), the variation option can be enabled. On randomly chosen quads, it flips the texture horizontally, automatically giving two variants of rendering the same quad.
Grass Quads Orientation#
To make grass quads repeat the relief, the following parameters are set up:
- Orientation. A grass quad can be oriented either along its own normal vector, or along the normal vector of its parent node (usually a terrain). However, on steep hills, grass oriented along the terrain normal can look unnatural (as the grass growth direction is up), so you will need to choose an appropriate value for the Angle parameter.
In the right picture below, the Angle parameter is set incorrectly and the grass grows almost perpendicular to the slope:Grass polygons oriented along normal of Grass ObjectGrass polygons oriented along normal of Terrain Object
- Angle. This parameter affects to grass distribution that varies depending on the ground slope angle. This parameter simulates the natural tendency of the grass to strike root in more flat places.
- Intersection. By reproducing the hilly landscape, it is essential that the grass follows the relief it grows on. For the grass to be rendered upon the ground, the following is necessary:
- Make the grass a child of the terrain or any arbitrary mesh (static or dynamic one). If there are some nodes in the hierarchy between a terrain or mesh node and grass, intersections will still be checked.
- Check the Intersection box.
Size of the Grass Field#
The grass can shoot as a few separate blades, or form a field of verdure stretching to the horizon. The size of the field determines the extent of rectangular area, which is covered with grass.
Step for Cell Division#
Visually attractive grass, however, may reduce performance if rendered as a whole. To allow rendering at interactive frame rates, the field is split into cells that form a grid. Each cell is estimated, whether it contributes to the grass field or stays grassless. Grassless cells are disregarded, while for the cells with grass all required computations are done. Depending on the density, blades are planted in the cell with random coordinates imitating the natural vegetation sprouting. The cells are rendered one by one, starting from the camera and further forward.
Controlling the step for dividing field into cells allows you to change the field size without affecting the set density for planting grass blades. On the other hand, if we change the step and consequently the number of cells without altering the density value, the number of grass quads rendered will be different.
If the Step value is too small and the maximum visibility distance is large, rendering of cells takes some time and the number of DIP calls increases significantly. On the other hand, a very high Step value causes performance spikes due to the increased number of objects per cell.
Each cell can also be subdivided into smaller parts by specifying the Subdivision value. Subdivision is usually required when the grass is used as a low-poly distant LOD of a forest created by using World Clutter or ObjectMeshClutter with a smaller cell size: the Subdivision parameter subdivides large grass cells so that they match smaller cells of a clutter. This way, positions of randomly scattered objects will coincide with positions of grass-based impostors. The range of available values is from 1 to 32.
Masks for the Grass Field#
Image Mask for the Grass Field#
To simulate a convincingly looking pasture that naturally blends with the rest of the virtual environment, the grass field can take an arbitrary outline gradually shaping and growing on the ground. Furthermore, the grass can grow chaotically and irregularly forming clumps of green and grassless patches, just like in the nature. To implement this effect, the mask is mapped on the terrain to determine the density distribution.
The mask is an RGBA8 texture. Each channel of this texture controls distribution of grass per vertical column of the grass diffuse texture (there can be up to 4 columns in the diffuse texture).
- Red channel specifies the areas of growth for the 1st texture column. If there are several grass clusters in a vertical column, they are randomly spread across the masked area.
- Green — for the 2nd texture column.
- Blue - for the 3rd texture column.
- Alpha - for the 4th texture column.
For every channel separately, areas where the grass should not grow have 0 channel value (no color or alpha data). Non-zero channel value creates a grassy area: the higher the value, the denser the grass grows.
The image mask is characterized with the following parameters:
- Threshold that controls starting from what density (according to the mask), the grass should grow.
- Min value and Max value that set the color density range.
- Flip X and Flip Y that flip the texture horizontally and vertically (respectively).
Minimum and Maximum Mask Values#
As a solution for advanced objects seeding and memory consumption optimization, a mask can be applied to the object partially. It means that different multiple objects can share the same mask, but use different levels.
By default, each channel of the image mask specifies the areas and the density of growth in the [0;255] color density range. However, by using Min value and Max value, you can modify a certain color density range, and the part of the mask that contains the specified density range will be used to seed the grass.
For example, there is the following R8 image mask:
Several grass objects that represent grass of different heights can share this mask as follows:
- In range from 100 to 200, density of the short grass can be stored. The part of the image mask, in which the specified range of color gradation is stored, will be applied for the short grass object.
- In range from 160 to 230, density of the grass of the medium height can be stored.
- In range from 190 to 255 density of the tall grass can be stored.
Vector masking allows creating roads, rivers, etc., with extremely high precision as it is independent of the mask texture resolution. A mesh-based mask can be set for grass to specify areas where grass should or should not grow.
A mesh mask can be inverted by toggling the Inverse flag.
Any area of a grass field can be animated by using Field Animation. Field Spacer is used to specify areas of grass that should not be rendered. Such areas of grass are specified by the Field mask (Parameters window -> Node tab -> Grass section).
See the example on animating grass with the animation field here.
Density of Growth and Grass Randomization#
To achieve the sufficient richness of a grass field, Density parameter can be tweaked. Density determines how many grass quads are to be rendered per square unit.
- The higher the value, the more grass quads are rendered and the more closely they are positioned. But it should be kept in mind, that the final result also depends on the Step of division and the number of cells it defines.
To randomly position grass quads across an area using the image mask, set up the Seed value, which is an integer value used to generate pseudo-random successions of numbers (in other words, a random seed). To get a random seed value, press the Randomize button.
To achieve the predominance of one type of the grass over the other types, you can use the Probability parameter. This parameter sets the grass rendering probability per diffuse texture column: you can specify a probability factor for each column of the diffuse texture separately.
For example, if you set the probability factor to 5 for the 1st column of the diffuse texture, the grass blades quads stored in slots of this column will appear more often.
Probability = 1
Probability for the 1st column = 5
As it was mentioned above, rendering all grass cells at once (and therefore thousands of grass polygons) drastically drops performance. So, to reduce the number of polygons rendered each frame, it is necessary to set the Spawn rate that controls the number of cells rendered per frame. The higher the spawn rate, the more likely a performance spike will occur. However, at a low spawn rate and FPS values, grass is generated right in front of your eyes (creating an effect of grass quads popping) as the camera approaches to the grass field.
Except reducing the number of cells rendered per frame, the grass field can be thinned out with a distance: you can check the Thinning box in order not to render random grass polygons across the grass Fade distance.
Choosing the optimal Step for cells division is also important when optimizing grass. For normal grass (that is used properly), the optimal step is in range [10, 25].
Using Grass as Low-Poly LOD#
When using a grass object as a low-poly LOD (for example, to create a distant LOD for a forest), the clutter and the grass cells must match. So, you need to make sure that the following parameters match:
- Seed of both objects so that they are positioned the same way.
- Size X and Size Y of both objects.
- Clutter Step matches the ratio of Step / Subdivision values of the grass.
- Orientation flag is enabled/disabled for both objects.
- Intersection flag is enabled/disabled for both objects.
- Flip X and Flip Y are enabled/disabled for both objects (in case the image mask is used).
- Inverse is enabled/disabled for both objects (in case the mesh mask is used).
- Min value and Max value of both objects (in case the image mask is used).
- Angle, Threshold, Density, and Probability of both objects.