Last active
January 27, 2024 07:55
-
-
Save Cyanilux/e7afdc5c65094bfd0827467f8e4c3c54 to your computer and use it in GitHub Desktop.
Experiments with DrawMeshInstanced and DrawMeshInstancedIndirect for drawing grass (over terrain)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// https://twitter.com/Cyanilux/status/1396848736022802435 | |
// Requires a specific shader to read the _PerInstanceData buffer at SV_InstanceID | |
// I use a shader made in Shader Graph, See : https://gist.github.com/Cyanilux/4046e7bf3725b8f64761bf6cf54a16eb | |
// Also note, there's no frustum culling involved in this example. Typically a compute shader is used for this. | |
using UnityEngine; | |
public class DrawGrass : MonoBehaviour { | |
public int count; // If DrawMeshInstanced is used, this is limited to a max of 1023. No limit for DrawMeshInstancedIndirect | |
public float range; // (terrain width) | |
public Terrain terrain; | |
public Mesh mesh; | |
public Material material; | |
private Matrix4x4[] matrices; | |
private MaterialPropertyBlock MPB; | |
private UnityEngine.Rendering.ShadowCastingMode shadowCasting = UnityEngine.Rendering.ShadowCastingMode.Off; | |
private bool receiveShadows = true; | |
private Bounds bounds; | |
private ComputeBuffer instancesBuffer; | |
private ComputeBuffer argsBuffer; | |
private struct InstanceData { | |
public Matrix4x4 matrix; | |
public static int Size() { | |
return sizeof(float) * 4 * 4; | |
} | |
} | |
public void OnEnable() { | |
MPB = new MaterialPropertyBlock(); | |
/* | |
If using DrawMeshInstanced, | |
matrices = new Matrix4x4[count]; | |
float range = 5f; | |
for (int i = 0; i < count; i++) { | |
float height = Random.Range(0.5f, 1.2f); | |
Vector3 position = new Vector3(Random.Range(-range, range), height * 0.5f, Random.Range(-range, range)); | |
Quaternion rotation = Quaternion.Euler(0, Random.Range(0f, 360f), 0); | |
Vector3 scale = new Vector3(1, height, 1); | |
matrices[i] = Matrix4x4.TRS(position, rotation, scale); | |
} | |
*/ | |
// If using DrawMeshInstancedIndirect, | |
bounds = new Bounds(transform.position, new Vector3(101, 1, 101)); | |
InitializeBuffers(); | |
} | |
private void InitializeBuffers() { | |
// Args | |
uint[] args = new uint[5] { 0, 0, 0, 0, 0 }; | |
args[0] = (uint)mesh.GetIndexCount(0); | |
args[1] = (uint)count; | |
args[2] = (uint)mesh.GetIndexStart(0); | |
args[3] = (uint)mesh.GetBaseVertex(0); | |
argsBuffer = new ComputeBuffer(1, args.Length * sizeof(uint), ComputeBufferType.IndirectArguments); | |
argsBuffer.SetData(args); | |
// Instances | |
InstanceData[] instances = new InstanceData[count]; | |
for (int i = 0; i < count; i++) { | |
InstanceData data = new InstanceData(); | |
//Vector3 position = new Vector3(Random.Range(-range, range), 0, Random.Range(-range, range)); | |
Vector3 position = new Vector3(Random.Range(0, range), 0, Random.Range(0, range)); | |
float height = Random.Range(0.3f, 0.9f); | |
float y = terrain.SampleHeight(position); | |
//position.y = (height - 1) * 0.5f; | |
position.y = y + (height) * 0.5f; | |
Quaternion rotation = Quaternion.Euler(0, Random.Range(0f, 360f), 0); | |
Vector3 scale = new Vector3(1, height, 1); | |
data.matrix = Matrix4x4.TRS(position, rotation, scale); | |
instances[i] = data; | |
} | |
instancesBuffer = new ComputeBuffer(count, InstanceData.Size()); | |
instancesBuffer.SetData(instances); | |
material.SetBuffer("_PerInstanceData", instancesBuffer); | |
} | |
public void Update() { | |
//Graphics.DrawMeshInstanced(mesh, 0, material, matrices, count, MPB, shadowCasting, receiveShadows); | |
//bounds = new Bounds(transform.position, new Vector3(101, 1, 101)); | |
Graphics.DrawMeshInstancedIndirect(mesh, 0, material, bounds, argsBuffer, 0, MPB, shadowCasting, receiveShadows); | |
} | |
private void OnDisable() { | |
if (instancesBuffer != null) { | |
instancesBuffer.Release(); | |
instancesBuffer = null; | |
} | |
if (argsBuffer != null) { | |
argsBuffer.Release(); | |
argsBuffer = null; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment