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右邊的小箭頭就可以看到。
在這裏插入圖片描述
優勢也很明顯:
  打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的使用之後會講,今天簡單給大家介紹一下這兩種方式。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章