对于大量相同模型的性能优化,在shader方法,可以用gpu instancing方式。
例如这个简单阴影计算的pass,注意里面有很多UNITY_开头的命令
//投影的计算Pass
Pass
{
Name "Shadow"
Stencil
{
Ref 0
Comp equal
Pass incrWrap
Fail keep
ZFail keep
}
Blend SrcAlpha OneMinusSrcAlpha
ZWrite Off
Offset -1, 0
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_instancing
#pragma multi_compile ROOTON_BLENDOFF ROOTON_BLENDON_CROSSFADEROOTON ROOTON_BLENDON_CROSSFADEROOTOFF ROOTOFF_BLENDOFF ROOTOFF_BLENDON_CROSSFADEROOTON ROOTOFF_BLENDON_CROSSFADEROOTOFF
#include "UnityCG.cginc"
#include "GPUSkinningInclude.cginc"
#include "Lighting.cginc"
struct appdata
{
float4 vertex : POSITION;
//阴影计算的Pass不需要通过UV来采样贴图,所以不需要
//float2 uv : TEXCOORD0;
//TEXCOORD1和TEXCOORD2需要通过skin2方法来解析贴图中存放的数据信息,顺序必须对应
float4 uv2 : TEXCOORD1;
float4 uv3 : TEXCOORD2;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f
{
float4 pos : SV_POSITION;
fixed4 color : COLOR;
//float2 uv : TEXCOORD0;
};
// float4 _LightDir;
float4 _ShadowColor;
// fixed _ShadowFalloff;
UNITY_INSTANCING_BUFFER_START(Prop)
UNITY_DEFINE_INSTANCED_PROP(float, _shadowY)
UNITY_INSTANCING_BUFFER_END(Prop)
float3 ShadowProjectPos(float4 vertPos)
{
float3 shadowPos;
float3 worldPos = mul(unity_ObjectToWorld, vertPos).xyz;
float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
float y = UNITY_ACCESS_INSTANCED_PROP(Prop,_shadowY);
shadowPos.y = min(worldPos.y, y);
shadowPos.xz = worldPos.xz - lightDir.xz * max(0, worldPos.y - y) / lightDir.y;
return shadowPos;
}
v2f vert(appdata v)
{
UNITY_SETUP_INSTANCE_ID(v);
v2f o;
//通过skin2得到顶点在物体自身坐标系的位置信息
float4 position = skin4(v.vertex, v.uv2, v.uv3);
float3 shadowPos = ShadowProjectPos(position);
o.pos = UnityWorldToClipPos(shadowPos);
// float3 center = float3(unity_ObjectToWorld[0].w, _LightDir.w, unity_ObjectToWorld[2].w);
// shadowPos.y=0;
// fixed falloff = 1 - saturate(distance(shadowPos, center) * _ShadowFalloff);
o.color = _ShadowColor;
// o.color.a *= falloff;
return o;
}
fixed4 frag(v2f i) : SV_Target
{
return i.color;
}
ENDCG
}
第一要添加 #pragma multi_compile_instancing 编译参数
其次,在appdata中最下面加上 UNITY_VERTEX_INPUT_INSTANCE_ID
第三,在顶点计算里面,最上面加 UNITY_SETUP_INSTANCE_ID(v);
如果你还有一些你自己定义的参数,可以这样定义参数
UNITY_INSTANCING_BUFFER_START(Prop)
UNITY_DEFINE_INSTANCED_PROP(float, _shadowY)
UNITY_INSTANCING_BUFFER_END(Prop)
里面的Prop表示这个Buffer的名字,你也可以换成其他
然后你用这个_shadowY的时候,需要这样调用:UNITY_ACCESS_INSTANCED_PROP(Prop,_shadowY);来获取_shadowY的值;
最后最重要的是外部 c#程序怎么设置这个_shadowY参数,最普通的设置是通过Renderer的material去调用setFloat来设置它,但是这种会造成pass增加,达不到我们想要的性能。
所以应该用MaterialPropertyBlock方式来设置_shadowY参数。
就算你的shader中有多个pass,有很多个相同的3d模型,drawCall也不会增加。当然drawCall增加可能是用了SkinnedMeshRenderer的原因,你想办法转成MeshRenderer,这不是这篇文章的重点,具体可以搜索GPUSkinning。