Unity multi_compile_instancing

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

你可能感兴趣的:(Unity)