Unity multi

    技术2022-07-10  143

    对于大量相同模型的性能优化,在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。

    Processed: 0.009, SQL: 9