Grass geometry is defined in SRT files, exactly as tree models are. This means that they are defined and edited in the SpeedTree Modeler as if they were any other tree model (often as individual “tufts” of grass), subject to the same geometry, lighting, and wind definitions as tree models. The only restriction is that they must have a single LOD and a single material. Grass models also do not use billboards for the lowest LOD since there is no LOD in the grass system.
|Note: Be sure to select “Used as grass” in the Compiler application when compiling a grass model. It will help with optimization.|
While we call this the “grass system”, it may be used to populate the scene with any bit of ground cover like rocks, twigs, or leaves. If the Modeler app can load it, it'll go through the pipeline and can be used as a grass model.
This page is very similar to the tree culling page and will contain a lot of the same content.
The SDK is set up for the client application to stream in those grass instances that are visible and will automatically flush those instances that are no longer visible.
The SDK organizes the world as a series of cells. As the camera moves, cells go in and out of visibility. As cellls become visible, the SDK will provide a list of these cells that need to have their populations streamed in. Hence, the SDK will perform most efficiently when the client has the tree instances already organized by cells so that the data might be passed into the SDK without further on-the-fly processing.
In the reference application, grass cells are populated using a random number generator, but any population procedure will work.
There are several classes and structures you'll need to get started:
|Note: Unlike the tree stream/cull system, the main camera's CView class cannot be used directly. The far clipping plane is use to keep the grass's population restricted to a short distance. In the reference application, CMyApplication::StreamGrassPopulation() shows an example of deriving a grass CView object from the main CView by using CView::AdjustPerspectiveNearAndFar().|
|Note: For tree instances, one CVisibleInstances objects will handle a whole forest of different 3D tree base models. For grass instances, one CVisibleInstances object is needed per base grass type. This permits individual cell size tuning based on population density as well as streamlining the cell population procedure.|
These data structures are detailed here.
As shown in CMyApplication::StreamGrassPopulation(), the general procedure followed for each frame where the camera has moved is outlined below. The whole procedure happens very quickly, even for densly populated grass.
Per frame, the general procedure is as follows:
|Note: The reference application uses image-based masks to control grass density, but your application is completely free to implement population in any manner.|
While the list above outlines the general approach needed for efficient and accurate grass streaming and culling, there are several other important points.
You can pick different cell sizes per grass type and it can impact both CPU and GPU performance a great deal. Each grass model is culled in a separate call, so separate sizes are easily accomodated. In contrast to tree instances, grass instances are not individually culled. If the cell is in the frustum, then every instance in it will be rendered. This cuts down on CPU usage greatly, but can be hard on the GPU for high densities. To strike a balance, smaller cell sizes can be used. In fact, we recommend smaller cell sizes for high density grass, and larger sizes for sparse models like rocks or boulders.
By way of example, the reference application's example forest (modeled in feet) uses cell sizes of 20 for the high density grass, and up to 100 for the rocks and butterfly models.
The organization of multiple types of grass instance into an arbitrary collection of cells can easily lead to a great number of heap allocations if you're not careful. We leave it to the user to implement their own population approach that doesn't cause undue heap fragmentation if that's a requirement for your title or project.
To control the SDK's heap behavior, be sure to read about the Reserves System.