is there a more optimal way of drawing multiple meshes?

Hi there,

I’ve been working on ways to “grass and tree” non terrain meshes for months now, and so far the winner is using DrawMesh(), but this has problems.
The most noticeable being that one drawcall is needed for every mesh that’s rendered (I’m talking around 300 minimum here), and these could theoretically be all done with one drawcall.
Is there a way to “batch” these meshes and positions at runtime (they change as you move around the lanscape, so it has to be fast).
Here’s my function that renders the meshes.

void UpdateMultiMesh(){
	Vector3 vFront=pPos+gCam.forward;
	
	//moo
	pBigMesh=null;
	Vector3 vGrass;
	Camera vCam=gCam.camera;
	MaterialPropertyBlock vBlock=new MaterialPropertyBlock();
	float vSqrMaxDist=pMaxDist*pMaxDist;
	if(pGLength>0){
		for (int vGrassNum=0 ;vGrassNum<pGLength;vGrassNum++){
			vGrass=pGrasses[vGrassNum];
			Vector3 vDelta =vGrass-vFront;
			if (vDelta.sqrMagnitude<vSqrMaxDist){
				Graphics.DrawMesh(pMeshes[vGrassNum % pMeshCount],vGrass,pIdentity,pMats[pMatIDs[vGrassNum]],5,vCam,0,vBlock,false,true);
			}
		}
	}
}

I also tried building more complex single meshes by hand by adding vertices normals etc, but the speed was certainly no better. Again the code is below.

	void UpdateBigMesh(){
		if(pBigMesh==null){
			BuildBigMesh();
		}
		if(pGLength>0){
			GetBillboardVerts();	
			int vVert=0;
			int vVertRatio=4;
			if(pTris){
				vVertRatio=3;
			}
			Vector3[] vVerts=new Vector3[pGrasses.Length*vVertRatio];
			Vector3[] vNors=new Vector3[pGrasses.Length*vVertRatio];
			if(vVerts.Length>65000){
				Debug.Log("Too big! unity doesn't allow this :(");
				return;
			}
			for (int vGrassNum=0 ;vGrassNum<pGLength;vGrassNum++){
				pTempMesh=pMeshes[vGrassNum % pMeshCount];
				Vector3 vGrass=pGrasses[vGrassNum];
				vVert=vGrassNum*vVertRatio;
				pTempMesh.vertices.CopyTo(vVerts,vVert);
				pNormals.CopyTo(vNors,vVert);
				for(int vNum=0;vNum<vVertRatio;vNum++){
					vVerts[vVert+vNum]+=vGrass;
				}
			}
			pBigMesh.normals=vNors;
			pBigMesh.vertices=vVerts;
			int vMat=0;
			if(pMatID<pMats.Length){
				vMat=pMatID;
			}
			Graphics.DrawMesh(pBigMesh,Vector3.zero,pIdentity, pMats[vMat], 0);
		}
	}

Any pointers as to how to speed this up would be great, but the obvious thing seems be a queueing of all multi-meshes for just one draw call.

[Update: just figured out a combine meshes equivalent of the big mesh builder, but it’s actually slower than building by hand as above]

sharing :

	void UpdateOtherBigMesh(){
		//if(pBigMesh==null){
	        List<CombineInstance> combine = new List<CombineInstance>();
			CombineInstance vCombo=new CombineInstance();
	       Matrix4x4 vTrans=transform.localToWorldMatrix;
			
			int vVerts=0;
			Vector3 vPos;
	        for (int vGrassNum=0 ;vGrassNum<pGLength;vGrassNum++){
				pTempMesh=pMeshes[vGrassNum % pMeshCount];
				vVerts+=pTempMesh.vertices.Length;
				if(vVerts<65000){
		            vCombo.mesh = pTempMesh;
					vPos=pPosterGrasses[vGrassNum].pos;
					vTrans[0,3]=vPos.x;
					vTrans[1,3]=vPos.y;
					vTrans[2,3]=vPos.z;
		            vCombo.transform =vTrans;
		           	combine.Add(vCombo);
				}
	        }
			CombineInstance [] vRealCombo=combine.ToArray();
	        pBigMesh = new Mesh();
	        pBigMesh.CombineMeshes(vRealCombo);
		//}
		int vMat=0;
		if(pMatID<pMats.Length){
			vMat=pMatID;
		}
		Graphics.DrawMesh(pBigMesh,Vector3.zero,pIdentity, pMats[vMat], 0);
	}

Is there an answer to this? a method of grouping these drawcalls would revolutionise what I'm doing here :( polygon cout seems to be of little impact until I go stupid with a really complex model, so evidently it is quite literally the multitudes of seperate draw calls that has the frame rate punch.

I ran some experiments a year and a half ago, and found the same thing. Drawcalls would batch when I used game objects, but when I used DrawMesh() drawcalls would not batch. Note that objects will not batch if you have over 900 vertex attributes. Consider combining your meshes.

Geometry shaders can create new things, vertex shaders can only transform existing vertices. There may be some sufficient [web examples][1] that you can port. [1]: https://www.google.com/#q=animated+grass+vertex+shader

hmmm :( already tried google, I'm sure that perfect search term is out there, but I haven't found it. also... animating is cool, but peripheral (I can do that). I'm at the "grass - fast" stage not the "wave" one. thanks anyway

1 Answer

1

Depending on what you’re doing for grass and foliage, you might want to consider going down the vertex or geometry shader route. Maybe assign UV2 to something that can be used to procedurally animate the verts in shader.

where I'm at: (appologies to future readers, these links will be temporary) [grass_test2][1] [1]: http://www.gamevial.com/fullscreen.php?game=grass_test2&format=unity