using UnityEngine; using UnityEngine.Rendering; public class GPUGrassRenderer : MonoBehaviour { public Mesh grassMesh; // одна модель травинки (один submesh) public Material grassMaterial; // shader має підтримувати GPU instancing public ComputeShader computeShader; public int countX = 256; public int countZ = 256; public float sizeX = 50f; public float sizeZ = 50f; public float height = 0f; GraphicsBuffer matricesBuffer; GraphicsBuffer argsBuffer; int totalCount; void Start() { totalCount = countX * countZ; // 1) create structured buffer for matrices (float4x4 = 16 floats = 64 bytes) matricesBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, totalCount, 16 * sizeof(float)); // 2) args buffer (5 uints): indexCountPerInstance, instanceCount, startIndexLocation, baseVertexLocation, startInstanceLocation uint[] args = new uint[5] { (uint)grassMesh.GetIndexCount(0), (uint)totalCount, (uint)grassMesh.GetIndexStart(0), (uint)grassMesh.GetBaseVertex(0), 0u }; argsBuffer = new GraphicsBuffer(GraphicsBuffer.Target.IndirectArguments, 1, sizeof(uint) * args.Length); argsBuffer.SetData(args); // dispatch compute to fill matrices int kernel = computeShader.FindKernel("CSMain"); computeShader.SetInt("_CountX", countX); computeShader.SetInt("_CountZ", countZ); computeShader.SetFloat("_SizeX", sizeX); computeShader.SetFloat("_SizeZ", sizeZ); computeShader.SetFloat("_Height", height); computeShader.SetBuffer(kernel, "matrices", matricesBuffer); int threadGroups = Mathf.CeilToInt(totalCount / 64.0f); computeShader.Dispatch(kernel, threadGroups, 1, 1); // bind buffer to material for per-instance matrix access (if shader reads it) grassMaterial.SetBuffer("matrices", matricesBuffer); } void OnDisable() { matricesBuffer?.Dispose(); argsBuffer?.Dispose(); } void Update() { // set render params: use simple bounds so Unity can cull var bounds = new Bounds(transform.position, new Vector3(sizeX, 10f, sizeZ)); RenderParams rparams = new RenderParams(grassMaterial); // the shader must use UNITY_INDIRECT_DRAW_ARGS / UnityIndirect.cginc if it needs instance id access // call indirect draw rparams.worldBounds = new Bounds(transform.position, new Vector3(sizeX, 10f, sizeZ)); Graphics.RenderMeshIndirect(rparams, grassMesh, argsBuffer); // parameters: rparams, mesh, argsBuffer, commandCount=1, startCommand=0, worldBounds } }