GUP Instancing技术,在模型的批处理上相当有用,比如地形上大量的草,树等,当然对于这部分可以直接静态合批处理成一整个网格,但是顶点数目又会有65000个的限制。这时候GPU Instancing就有一定的优势(关键还支持材质球上属性的差异性,比如官网的例子中就提供了颜色值的差异性)。
官网地址:https://docs.unity3d.com/Manual/GPUInstancing.html
参考官网的例子写的测试效果(可以明显看到开启与不开启的Batches的差异是巨大的):
附上测试用Shader:
Shader "vitens/gpu instancing"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Tint("tint", Color) = (1,1,1,1)
_Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
_Specular ("Specular", Color) = (1, 1, 1, 1)
_Gloss ("Gloss", Range(8.0, 256)) = 20
}
SubShader
{
Tags { "RenderType"="Opaque" }
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_instancing //Use this to instruct Unity to generate instancing variants. It is not necessary for surface Shaders.
#include "UnityCG.cginc"
#include "Lighting.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float3 worldNormal : TEXCOORD1;
float3 worldPos : TEXCOORD2;
UNITY_VERTEX_INPUT_INSTANCE_ID // necessary only if you want to access instanced properties in fragment Shader.
};
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Diffuse;
fixed4 _Specular;
float _Gloss;
UNITY_INSTANCING_BUFFER_START(Props)
UNITY_DEFINE_INSTANCED_PROP(float4, _Tint)
UNITY_INSTANCING_BUFFER_END(Props)
v2f vert (appdata v)
{
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_TRANSFER_INSTANCE_ID(v, o); // necessary only if you want to access instanced properties in the fragment Shader
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(i); // necessary only if any instanced properties are going to be accessed in the fragment Shader.
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal));
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);
fixed4 col = tex2D(_MainTex, i.uv) * fixed4(ambient + diffuse + specular, 1.0) * UNITY_ACCESS_INSTANCED_PROP(Props, _Tint);
return col;
}
ENDCG
}
}
FallBack "Specular"
}
这里涉及到颜色的差异性则需要在shader外面单独传入参数,比如上面的_Tint属性,那么这个参数的声明则需要做如下处理
UNITY_INSTANCING_BUFFER_START(Props)
UNITY_DEFINE_INSTANCED_PROP(float4, _Tint)
UNITY_INSTANCING_BUFFER_END(Props)
而外部配合传入的参数则是采用Renderer的SetPropertyBlock方式,如下:
MeshRenderer[] renderers = GetComponentsInChildren<MeshRenderer>();
MaterialPropertyBlock props = new MaterialPropertyBlock();
for (int i = 0; i < renderers.Length; i++)
{
float r = Random.Range(0.0f, 1.0f);
float g = Random.Range(0.0f, 1.0f);
float b = Random.Range(0.0f, 1.0f);
props.SetColor("_Tint", new Color(r, g, b));
renderers[i].SetPropertyBlock(props);
}
OK 有了上述准备,再勾选上Inspect面板中的Enable GPU Instancing选项即可开启此效果。
看起来好像挺方便的样子,可以开心的玩耍了(建了超级多的球球,不采用此方法,Batches已经爆炸上万了):
正准备在工程中测试超级多带骨骼动画的士兵单位时,问题出现了,因为使用了蒙皮动画SkinnedMeshRenderer,官网上说的也很清楚,这个是不支持的:
所以得动手自己处理动画,替换掉SkinnedMeshRenderer组件,使用普通的MeshRenderer,从而也能采用GPUInstancing效果,下篇文章继续记录。