Unity shader_feature实现shader功能合并

前提:有个需求为切换顶点动画样式,例如有sin波动、cos波动、cos(θ+x)波动等等…
这时候你会怎么做?

是不是脚本中设置各种case,每个case加载对应的shader。这种方法可以用,但在实际开发中,根本不可取。因为可能会有大量Shader被编译,造成内存的大量占用。那么该怎么做呢?

常用的两种做法

  1.使用multi_complie或shader_feature来定义宏,根据不同的宏指令编译出多套Shader,Unity内建shader大体也是这么做的。
  2.有外部传入参数,在shader内部if判断,选择执行哪部分运算。
因为在shader种使用if、for很影响效率,所以第二种方法使用较少,用于case较少的时候。
接下来主要谈一下第一种方法的利与弊。

使用multi_complie

该指令可以达到上述效果,但他会无脑的进行组合编译,如果宏指令太多,会产生非常多的variant。
当你使用

#pragma multi_compile Red Green Blue

会产生三个variant,因为你定义了三个宏
当你使用

#pragma multi_compile Red Green Blue
#pragma multi_compile Pink Yellow 

会产生6个variant(RedPink,RedYellow,GreenPink,GreenYellow,BluePink,BlueYellow),因为他们之间会两两组合。

当你使用

#pragma multi_compile Red Green Blue
#pragma multi_compile Pink Yellow 
#pragma multi_complie Brown Purple Black

会产生323个variant。可见这个产生变体的数量规模是很大的。
如何查看一个shader产生的变体数量?
选中shader文件,点击compile and show code右边的小箭头就可以看到。
Unity shader_feature实现shader功能合并_第1张图片
优势也很明显:
  打ab包的时候,Unity会把shader中所有的multi_complie定义的变体全部打包,shader可以正常显示。但shader_feature就不那么尽人意了。
需要注意:
  其中指定的第一个关键字是默认生效的。
  使用multi_compile声明的都为全局关键字,一个项目种全局关键字最多只能有256个,内建shder大约已经用了60个。
  注意FallBack最好不要FallBack内建shader,也会增加很多变体。

使用shader_feature

该指令的效果和用法基本都与上面的一样。同时它就是为了multi_compile打包时的爆炸编译的问题。
实现:
shader代码

Shader "Custom/NewSurfaceShader" {
	Properties {
	}
	SubShader {
		Tags { "RenderType"="Opaque" }
		LOD 200
		
		pass
		{
			CGPROGRAM
			// Physically based Standard lighting model, and enable shadows on all light types
			//#pragma surface surf Standard fullforwardshadows

			#include "UnityCG.cginc"

			//#pragma shader_feature Red Green Blue
			#pragma multi_complie_local __ Red Green Blue
			//#pragma multi_compile __ Pink */
			// Use shader model 3.0 target, to get nicer looking lighting
			#pragma target 2.0
			#pragma vertex vert
			#pragma fragment frag

			struct v2f
			{
				float4 vertex:POSITION;
				fixed4 color:COLOR;

			};
		
			v2f vert(appdata_base i)
			{
				v2f o;
				o.vertex=UnityObjectToClipPos(i.vertex);
				o.color=fixed4(1,1,1,1);
				return o;
			}

			fixed4 frag(v2f i):SV_Target
			{
				#if Red
				return i.color=fixed4(1,0,0,1);
				#elif Blue
				return i.color=fixed4(0,0,1,1);
				#else
				return i.color=fixed4(0,1,0,1);
				#endif
			}
			ENDCG
		}
	}
	//FallBack "Diffuse"
}

C#代码进行外部控制:

/*
	author:@
	Last modified data:
	funtion todo:
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Shader_featureTest : MonoBehaviour {
    public enum ColorMode
    {
        Red,
        Blue,
        Green
    }

    public ColorMode colorMode = ColorMode.Green;
    Material mt;
	// Use this for initialization
	void Start () {
        mt = GetComponent<MeshRenderer>().material;
	}
	
	// Update is called once per frame
	void Update () {
        print(colorMode.ToString());
        switch (colorMode)
        {

            case ColorMode.Red:
               
                mt.shaderKeywords = new string[1] { colorMode.ToString() };
                break;
            case ColorMode.Blue:
                mt.shaderKeywords = new string[1] { colorMode.ToString() };
                
                break;
            case ColorMode.Green:
                mt.shaderKeywords = new string[1] { colorMode.ToString() };
               
                break;
            default:
                break;
        }
    }
}

把材质赋予随便一个物体上,运行时切换就可以达到效果。在multi_complie下使用EnableKeywords和DisableKeywords有事会失败,这里建议直接使用shaderKeywords属性来设置(希望有大神可以告诉为什么那两个方法会失败,很是疑惑

看到这么一条,还没有尝试:
在脚本这么用
Material.EnableKeyword 和 DisableKeyword
Shader.EnableKeyword 和 DisableKeyword 控制keyword起效
注意:
  5.4中用Shader.EnableKeyword设置了全局使用默认的key, 用Material.EnableKeywor设置单个不使用默认值无效
  用Shader.EnableKeyword设置了全局不使用默认的key, 用Material.EnableKeywor设置单个使用默认值起效
  EnableKeyword 和 DisableKeyword 最好组合使用 比如一组有三个,必须写1个enable2个disable

不管有多少个关键字,总会产生一个variant,大大降低了编译出来的shader变体。
但是打ab包的时候,如果shader和所使用的材质不在一个ab包内,那么材质的shader中所有的变体都不会进入ab包中。在打包后代码中动态切换keywords,要把使用到的shader变体也打入包中才可以。
为了让变体在运行时可以更好的处理,Unity5.x之后引入了一个shader变体集合:ShaderVariantCollection,ShaderVariantCollection的使用之后会讲,今天简单给大家介绍一下这两种方式。

你可能感兴趣的:(shader,unity,内存优化,c#)