Unity随记(二) GPU Instancing的使用

标签: Shader  Unity  游戏开发  unity3d  shader

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效果,下篇文章继续记录。 

版权声明:本文为Vitens原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Vitens/article/details/104879096