A procedural asset scattering system built with BabylonJS. It can render millions of blades of grass with LoD and cute butterflies.
The instance buffers are generated using compute shaders when available, otherwise they are generated on the CPU. To see the CPU only version, checkout the branch cpu
.
Main demo with procedural terrain, lod, collisions, butterflies and trees: here
Large flat grass field with lod and butterflies: here
Experimentation on a spherical world: here
Minimal example of a dense patch of grass: here
If you can't run the demo, there is a video on YouTube here.
All demos are WebGPU compatible, simply add ?webgpu
to the URL to use WebGPU instead of WebGL. If your browser doesn't support WebGPU, it will throw an error in the console.
The files for the asset scattering are located in the folder src/ts/instancing
.
There are 3 types of patches: InstancePatch
, ThinInstancePatch
and HierarchyInstancePatch
. They all use the same interface IPatch
so they are interchangeable.
The first thing to do is to create a matrix buffer for the patch which will contain all positions, scaling and rotations of the instances. There is a utility function in each patch class to create a simple square patch (look at src/ts/minimal.ts
to see it in action).
When the buffer is created, it is just a matter of passing it to the patch constructor and then calling createInstances()
on the instance with the mesh you want to instantiate.
If you want raw speed, prefer ThinInstancePatch
. It is faster because it uses a single draw call for all the instances. However, you give up the ability to have collisions on your instances.
On the other hand, InstancePatch
is slower, but you get a list of InstancedMesh
that you can tweak individually. This is useful if you want to have collisions on your instances.
HierarchyInstancePatch
is a special case of InstancePatch
that allows to instantiate hierarchies of meshes. It can be useful for complex objects or GLTF models.
This is completely optional, but you can have LoD with your patches by using a PatchManager
.
The Manager takes an array of meshes describing the different LoD levels of your base mesh and a function that defines the LoD as a function of the distance to the camera.
Then, you simply add your patches to the manager and call update()
on the manager every frame. The manager will take care of updating the LoD of your patches.
This may result in frame drops if your patches have a lot of thin instances, this might be solved in the future but this is not a priority for now.
To enable collisions on an InstancePatch
, simply set checkCollisions=true
on your base mesh. Then moving objects with moveWithCollisions
will respect the collisions.
The grass rendering is based on this video from Simon Dev. It is itself based on this gdc conference.
The wind sound effect is from this video.
npm install
npm run serve