- Home /
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 Draw$$anonymous$$esh() drawcalls would not batch. Note that objects will not batch if you have over 900 vertex attributes. Consider combining your meshes.
hi robertbu , thanks, I'm at that point but can't combine the sheer magnitude of things that I need here easily.
Williamleu that sounds like a brilliant idea re vertex or geometry shaders, do you have any pointers on that?
Can a vertex shader grab one vertex and bang out 4 verts with the approprate data to render a plane at the given point?
Can a surface shader be mucked about with like a vertex/fragment shader? I built this rather dodgy shader based on http://en.wikibooks.org/wiki/Cg_Program$$anonymous$$g/Unity/Billboards
Shader "Cg shader for billboards" {
Properties {
_$$anonymous$$ainTex ("Texture Image", 2D) = "white" {}
}
SubShader {
Tags{"Queue"="Transparent"}
Cull Off
ZWrite Off
Alphatest Greater[_CutOff]
Blend SrcAlpha One$$anonymous$$inusSrcAlpha
Pass {
CGPROGRA$$anonymous$$
#pragma vertex vert
#pragma fragment frag
uniform half4 _LightColor0;
// User-specified uniforms
uniform sampler2D _$$anonymous$$ainTex;
struct vertexInput {
float4 vertex : POSITION;
float4 tex : TEXCOORD0;
};
struct vertexOutput {
float4 pos : SV_POSITION;
float4 tex : TEXCOORD0;
};
vertexOutput vert(vertexInput input) {
vertexOutput output;
output.pos = mul(UNITY_$$anonymous$$ATRIX_P,mul(UNITY_$$anonymous$$ATRIX_$$anonymous$$V, float4(0.0, 0.0, 0.0, 1.0))+float4(input.vertex.x, input.vertex.y, -input.vertex.z, 0.0));
output.tex =input.tex;
return output;
}
float4 frag(vertexOutput input) : COLOR {
//float4 c=
return tex2D(_$$anonymous$$ainTex, float2(input.tex.xy)) *float4(_LightColor0.r,_LightColor0.g,_LightColor0.b,1);// * ( atten * 2);;
}
ENDCG
}
}
}
Then changed the frag for a surf, and added my own lighting model, no cigar. From what I can tell, surf uses "vertex" in place of "pos", but they seem to use different world/camera space coordinates as the results are wong.
$$anonymous$$y limited knowledge tells me that geometry shaders is that they can and more but are dx11 only, and the only samples I've found online don't work here so I'm nervous.
I'm sorry if my questions are stupid. The doccumentation that I've found online for shader development hasn't really helped me "get" shaders particularly, even if it has helped me build some quite nice ones.
Geometry shaders can create new things, vertex shaders can only transform existing vertices. There may be some sufficient web examples that you can port.
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
Answer by WilliamLeu · Jul 23, 2014 at 02:43 AM
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.
hi WilliamLeu,
If "vertex shader" means vertex/fragment shader I'm already going down this route:
Properties{
_Color ("$$anonymous$$ain Color", Color) = (1,1,1,1)
_$$anonymous$$ainTex ("Base (RGBA) ", 2D) = "white" {}
_CutOff ("Cut Off", Range (0,1)) = 0.7
_Speed ("Speed",Float) = .5
_Power ("Power",Float) = .1
_Translucency("Translucency",Range(0,1)) = .5
}
SubShader{
Cull Off
Tags { "RenderType"="Transparent" }
AlphaTest Greater [_CutOff]
LOD 200
CGPROGRA$$anonymous$$
#pragma surface surf Wrap$$anonymous$$oo vertex:vert alphatest:_CutOff addshadow halfasview
float4 _Color;
sampler2D _$$anonymous$$ainTex;
float _Speed,_Power;
half _Translucency;
struct Input {
float4 light;
float2 uv_$$anonymous$$ainTex:TEXCOORD0;
float alpha;
};
void vert(inout appdata_full v, out Input o) {
UNITY_INITIALIZE_OUTPUT(Input,o);
float vPos= v.vertex.x+v.vertex.y/100;
float fsin,fcos;
float vOffset=vPos+(_Time.z*_Speed);
fsin=sin(vOffset);
fcos=cos(vOffset);
v.vertex.z+=fcos*_Power*v.vertex.y;
v.vertex.x+=fsin*_Power*v.vertex.y;
}
void surf (Input IN, inout SurfaceOutput o) {
fixed4 vAlb = tex2D (_$$anonymous$$ainTex, IN.uv_$$anonymous$$ainTex);
o.Albedo = vAlb.rgb*_Color;
o.Alpha = vAlb.a;
}
half4 LightingWrap$$anonymous$$oo (SurfaceOutput s, half3 dir, half atten) {
half4 c = _LightColor0 * ( atten * 2)+UNITY_LIGHT$$anonymous$$O$$anonymous$$_A$$anonymous$$BIENT;
half NorLight = dot (s.Normal, dir);
c = lerp(c*NorLight,c,_Translucency);
c.rgb *= s.Albedo;
c.a = s.Alpha;
return c;
}
ENDCG
}
}
I include this rather over complex shader just to reiterate that this really isn't about waving, waving is cool.
If you mean fixed function shaders, maybe you can help me.
Ths far I haven't found a fixed function shader that can read lighting without reading the normals of it's mesh, and thus lighting stupidly as the camera pans arund it and it's angle to the light source changes.
Geometry shaders are dx11 to my knowledge (is this right?), and whilst my pc handles dx11 with love, it doesn't in unity or the webplayer without serious fiddling, and I'm not keen to encourage our web audience to go into their nVidia settings to configure internet explorer at this point.
$$anonymous$$aybe you mean something else? Conceptually I don't struggle with the idea of using something more akin to a mesh-based particle rendering system for grass, which may indeed be something that is done in the shader, as opposed to with meshes, but thus far, attempts of this sort in unity have not been very successful here.
where I'm at: (appologies to future readers, these links will be temporary)