合并Shader

<轉(zhuǎn)>我也忘了轉(zhuǎn)自哪里,抱歉,感謝原作者

《合并Shader》系列旨在介紹一些在保證功能不打折的情況下精簡(jiǎn)Shader數(shù)量的方法返弹,其遵循的原理就是把相似功能的Shader文件合并在一個(gè)文件里京闰。在掌握了這些技能后衙吩,研發(fā)團(tuán)隊(duì)能在極致情況下做到把所有的Shader文件合并成一個(gè),如Unity 5.x的Standard著色器盖彭。本系列會(huì)從簡(jiǎn)到繁的方式來依次遍歷林林總總的合并方法寺滚,希望大家有所收獲喻圃。


在數(shù)學(xué)中我們學(xué)習(xí)過:把多項(xiàng)式中的同類項(xiàng)合并成一項(xiàng)叫做合并同類項(xiàng)。同理猎荠,提取Shader的相似部分坚弱,把多個(gè)Shader合并成一個(gè)就叫做Shader的合并,也叫合并Shader关摇,偶爾也會(huì)引用數(shù)學(xué)的名詞來稱呼他為Shader的合并同類項(xiàng)荒叶。

Shader的合并方式有很多,根據(jù)不同的合并技能和方法可以劃分為不同的派系输虱。今天優(yōu)先介紹一種不太常見些楣、但又很實(shí)用的派系,往下看宪睹。

在Shader的合并方法中愁茁,MaterialPropertyDrawer(屬性定義)可以說是自成一派,但又與其他派系有著千絲萬縷的關(guān)系亭病,今天我們就先拿它來開刀鹅很。

在此之前我想補(bǔ)充一點(diǎn):對(duì)于Shader的合并,首先讓人想到的應(yīng)該是宏定義罪帖,相信宏定義也是大家應(yīng)用最廣促煮、最先接觸的。(畢竟由于GPU的特殊性整袁,Shader里常常通篇都充滿了各種宏定義菠齿。)當(dāng)然,該系列會(huì)對(duì)宏定義有所介紹葬项,它可是Shader合并里功高蓋世的重要角色泞当,很多地方都會(huì)有它的身影。但對(duì)它的介紹不在這一篇,也許會(huì)是下一篇襟士。因?yàn)槲艺J(rèn)為在合并Shader的眾多方法中盗飒,最簡(jiǎn)單的不是它,而是使用Unity已經(jīng)預(yù)先定制好的幾種MaterialPropertyDrawer的方式陋桂。只用修改兩行代碼逆趣,就可以搞定一類Shader的合并,該方法主要用來合并那些只是渲染狀態(tài)不一樣的Shader嗜历。


◆◆◆
初次簡(jiǎn)單使用

一個(gè)完整的Shader宣渗,它的渲染狀態(tài)變量有很多種,由于不是每一種狀態(tài)的改變都能很明顯地看到結(jié)果梨州。那么作為初次使用痕囱,我們就優(yōu)先選擇一種最明顯的、最易懂的狀態(tài)作為測(cè)試用例—ZTest暴匠,即深度比較鞍恢。對(duì)ZTest不太了解的朋友,可以看看官方的學(xué)習(xí)文檔或者查看一下相關(guān)技術(shù)書籍每窖,我就不在這里具體介紹這個(gè)狀態(tài)了帮掉。

1.1 首先我們選用的是Unity官方提供的一個(gè)最常用Shader

Normal-Diffuse.shader(Legacy Shaders/Diffuse)

1.2 在屬性列表(Properties)中添加一行

[Enum(UnityEngine.Rendering.CompareFunction)] _ZTest ("ZTest", Float) = 2

1.3 在SubShader中添加一行

ZTest [_ZTest]

1.4 完整的Shader

Shader "ShaderCombine/01.ShaderCombineSimpleZTest"{    Properties {        _Color ("Main Color", Color) = (1,1,1,1)        _MainTex ("Base (RGB)", 2D) = "white" {}        [Enum(UnityEngine.Rendering.CompareFunction)] _ZTest ("ZTest", Float) = 2    }    SubShader {        Tags { "RenderType"="Opaque" }        LOD 200        ZTest [_ZTest]        CGPROGRAM        #pragma surface surf Lambert        sampler2D _MainTex;        fixed4 _Color;        struct Input {            float2 uv_MainTex;        };        void surf (Input IN, inout SurfaceOutput o) {            fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;            o.Albedo = c.rgb;            o.Alpha = c.a;        }        ENDCG    }    Fallback "Legacy Shaders/VertexLit"}

只用添加這兩行代碼,我們就可以在Inspector面板中控制使用該Shader物體的深度測(cè)試方法窒典。

1.5 直觀展示
在Unity中的測(cè)試示例是這樣的:

當(dāng)然大家也可以通過這個(gè)示例試驗(yàn)下每一種深度測(cè)試的方法是否與你心中所想蟆炊、或之前所學(xué)的有所沖突。


◆◆◆
再次深入使用

在上面的例子中瀑志,我們只使用了深度測(cè)試涩搓。但單單一個(gè)深度測(cè)試肯定滿足不了我的需求,我們還需要更多劈猪、更多的狀態(tài)缩膝,比如背面剔除、混合模式等等岸霹。

在這一節(jié)中疾层,我列舉出了一些常用的狀態(tài)控制量,對(duì)于一些不常用的模板等贡避,就不在這里列舉示启。大家可以依葫蘆畫瓢奏夫,因?yàn)榇蟛糠謴睦碚撋蟻碚f都是可行的蟆盐。

2.1 一個(gè)大而全的簡(jiǎn)單示例Shader如下:

Shader "ShaderCombine/02.ShaderCombineCommonState"{    Properties {        _Color ("Main Color", Color) = (1,1,1,1)        _MainTex ("Base (RGB)", 2D) = "white" {}        [Enum(UnityEngine.Rendering.CullMode)] _Cull ("Cull Mode", Float) = 1        [Enum(Off,0,On,1)] _ZWrite ("ZWrite", Float) = 1        [Enum(UnityEngine.Rendering.CompareFunction)] _ZTest ("ZTest", Float) = 1        [Enum(UnityEngine.Rendering.BlendMode)] _SourceBlend ("Source Blend Mode", Float) = 2                [Enum(UnityEngine.Rendering.BlendMode)] _DestBlend ("Dest Blend Mode", Float) = 2    }    SubShader {        Tags { "RenderType"="Opaque" }        LOD 200        ZTest [_ZTest]        Cull [_Cull]        ZWrite [_ZWrite]        ZTest [_ZTest]        Blend [_SourceBlend] [_DestBlend]        CGPROGRAM        #pragma surface surf Lambert        sampler2D _MainTex;        fixed4 _Color;        struct Input {            float2 uv_MainTex;        };        void surf (Input IN, inout SurfaceOutput o) {            fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;            o.Albedo = c.rgb;            o.Alpha = c.a;        }        ENDCG    }    Fallback "Legacy Shaders/VertexLit"}

2.2 直觀展示
應(yīng)用效果:


◆◆◆
有限自定義

在上面的示例中啄踊,我們都是使用Unity預(yù)先定義好的一些枚舉類型,比如UnityEngine.Rendering.CompareFunction杀捻、UnityEngine.Rendering.BlendMode等井厌。這些定義好的類型把每個(gè)狀態(tài)可能的選項(xiàng)都一一列舉了,但有時(shí)候我們并不需要這么多選項(xiàng),或者說我們并不希望給美術(shù)列舉出所有的可選項(xiàng)仅仆,畢竟很多選項(xiàng)我們可能做完整個(gè)項(xiàng)目或者幾個(gè)項(xiàng)目都不會(huì)使用到器赞,而選項(xiàng)過多也會(huì)帶來很的麻煩∧拱荩或者換一個(gè)說法港柜,我希望我們的功能使用起來簡(jiǎn)單易懂、不易出錯(cuò)并且可控咳榜,那就需要我們開發(fā)做更多的工作夏醉,去掉那些"無用"的選項(xiàng)。其實(shí)說到底無非就是:我們能否自定義每個(gè)狀態(tài)的選項(xiàng)呢涌韩?答案當(dāng)然是可以的畔柔。

在給出自定義方式前,我們先來熟悉一下Unity給我們提供的這幾個(gè)枚舉類型臣樱。

3.1 剔除模式

UnityEngine.Rendering.CullMode:public enum CullMode{           Off   = 0,    //Disable culling.           Front = 1,    //Cull front-facing geometry.           Back  = 2     //Cull back-facing geometry.}

3.2 比較方式
該比較方式通用與深度比較和模板比較

UnityEngine.Rendering.CompareFunction//Depth or stencil comparison function.public enum CompareFunction{    Disabled     = 0,   //Depth or stencil test is disabled.    Never        = 1,   //Never pass depth or stencil test.    Less         = 2,   //Pass depth or stencil test when new value is less than old one.    Equal        = 3,   //Pass depth or stencil test when values are equal.    LessEqual    = 4,   //Pass depth or stencil test when new value is less or equal than old one.    Greater      = 5,   //Pass depth or stencil test when new value is greater than old one.    NotEqual     = 6,   //Pass depth or stencil test when values are different.    GreaterEqual = 7,   //Pass depth or stencil test when new value is greater or equal than old one.    Always       = 8    //Always pass depth or stencil test.}

3.3 混合模式

UnityEngine.Rendering.BlendMode//Blend mode for controlling the blending.public enum BlendMode{    Zero             = 0,   //Blend factor is (0, 0, 0, 0).    One              = 1,   //Blend factor is (1, 1, 1, 1).    DstColor         = 2,   //Blend factor is (Rd, Gd, Bd, Ad).    SrcColor         = 3,   //Blend factor is (Rs, Gs, Bs, As).    OneMinusDstColor = 4,   //Blend factor is (1 - Rd, 1 - Gd, 1 - Bd, 1 - Ad).    SrcAlpha         = 5,   //Blend factor is (As, As, As, As).    OneMinusSrcColor = 6,   //Blend factor is (1 - Rs, 1 - Gs, 1 - Bs, 1 - As).    DstAlpha         = 7,   //Blend factor is (Ad, Ad, Ad, Ad).    OneMinusDstAlpha = 8,   //Blend factor is (1 - Ad, 1 - Ad, 1 - Ad, 1 - Ad).    SrcAlphaSaturate = 9,   //Blend factor is (f, f, f, 1); where f = min(As, 1 - Ad).    OneMinusSrcAlpha = 10   //Blend factor is (1 - As, 1 - As, 1 - As, 1 - As).}

3.4 有限的自定義
在上面三個(gè)小小節(jié)中释树,我們了解了Unity自身提供的狀態(tài)選項(xiàng),而且每一個(gè)狀態(tài)選項(xiàng)后都強(qiáng)制賦上了相應(yīng)的數(shù)值擎淤,這是有原因的。因?yàn)槲覀儗懞玫腟hader不管怎樣秸仙,都要首先經(jīng)過Unity的編譯等處理轉(zhuǎn)換為目標(biāo)平臺(tái)的著色器語言嘴拢。而Unity自己的Shader編譯器,在沒有源碼的情況下我們是無法修改的寂纪,也就是說我們不能隨意更改這些狀態(tài)的數(shù)值席吴。其實(shí)我們修改的這些狀態(tài)值都是給Unity的Shader編譯器看的,而編譯器對(duì)狀態(tài)的數(shù)值理解是固化好的捞蛋。因此孝冒,雖然我們可以自定義這些狀態(tài)選項(xiàng),但編譯器也不會(huì)任由我們隨意定義拟杉,這就好比戴著鐐銬跳舞庄涡,雖然有限制,但我們依然可以跳出優(yōu)美的舞蹈搬设。

自定義非常的簡(jiǎn)單穴店,我們可以減少選項(xiàng)的數(shù)量,但是不能改變每一項(xiàng)的值拿穴,這就要求我們強(qiáng)行給每一個(gè)值賦上對(duì)應(yīng)的值泣洞,依然還是用深度測(cè)試實(shí)驗(yàn),如下所示:

這是之前的:

[Enum(UnityEngine.Rendering.CompareFunction)] _ZTest ("ZTest", Float) = 2

這是自定后的:

[Enum(Less,2,Greater,5)] _ZTest ("ZTest", Float) = 2

選項(xiàng)與數(shù)值全部使用逗號(hào)分隔默色,該示例中我只給出了兩個(gè)選項(xiàng)球凰,小于和大于,便于直觀查看。

完整Shader如下:

Shader "ShaderCombine/03.ShaderCombineCustomState"{    Properties {        _Color ("Main Color", Color) = (1,1,1,1)        _MainTex ("Base (RGB)", 2D) = "white" {}        [Enum(Less,2,Greater,5)] _ZTest ("ZTest", Float) = 2    }    SubShader {        Tags { "RenderType"="Opaque"}        LOD 200        ZTest [_ZTest]        CGPROGRAM        #pragma surface surf Lambert        sampler2D _MainTex;        fixed4 _Color;        struct Input {            float2 uv_MainTex;        };        void surf (Input IN, inout SurfaceOutput o) {            fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;            o.Albedo = c.rgb;            o.Alpha = c.a;        }        ENDCG    }    Fallback "Legacy Shaders/VertexLit"}

3.5 直觀展示
在Unity中的樣子是這樣的:

細(xì)心的讀者可能已經(jīng)發(fā)現(xiàn)我們?cè)诘诙鹿?jié)中的完整示例中就有使用自定義呕诉,就是里面的那個(gè)寫深度_ZWrite選項(xiàng)缘厢,因?yàn)闆]有在Unity里找到相應(yīng)的枚舉值,就直接使用了自定義义钉,反正只要保證數(shù)值正確就可以任意發(fā)揮使用昧绣。更多的使用和應(yīng)用場(chǎng)景就等你們?nèi)グl(fā)現(xiàn)了,我這只是拋磚引玉捶闸。

PS:其實(shí)最初是想花一整章篇幅來講解MaterialPropertyDrawer的各種使用夜畴,但其內(nèi)部的擴(kuò)展空間還比較廣泛。寫下來篇幅太長(zhǎng)删壮,而太長(zhǎng)的篇幅贪绘,閱讀起來也比較麻煩,所以還是拆成幾章篇幅來慢慢絮叨吧央碟,同時(shí)也遵從一次只講一個(gè)問題税灌。

以上便是MaterialPropertyDrawer的應(yīng)用場(chǎng)景之一,對(duì)于MaterialPropertyDrawer的應(yīng)用亿虽,在后續(xù)的篇章中也還會(huì)陸續(xù)出現(xiàn)菱涤。筆者計(jì)劃把MaterialPropertyDrawer應(yīng)用當(dāng)成《合并Shader》系列中的一個(gè)分支,當(dāng)然《合并Shader》系列不會(huì)僅且只有這一個(gè)分支的洛勉,O(∩_∩)O哈哈~粘秆,歡迎大家持續(xù)關(guān)注!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末收毫,一起剝皮案震驚了整個(gè)濱河市攻走,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌此再,老刑警劉巖昔搂,帶你破解...
    沈念sama閱讀 222,681評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異输拇,居然都是意外死亡摘符,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門策吠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來议慰,“玉大人,你說我怎么就攤上這事奴曙”鸢迹” “怎么了?”我有些...
    開封第一講書人閱讀 169,421評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵洽糟,是天一觀的道長(zhǎng)炉菲。 經(jīng)常有香客問我堕战,道長(zhǎng),這世上最難降的妖魔是什么拍霜? 我笑而不...
    開封第一講書人閱讀 60,114評(píng)論 1 300
  • 正文 為了忘掉前任嘱丢,我火速辦了婚禮,結(jié)果婚禮上祠饺,老公的妹妹穿的比我還像新娘越驻。我一直安慰自己,他們只是感情好道偷,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,116評(píng)論 6 398
  • 文/花漫 我一把揭開白布缀旁。 她就那樣靜靜地躺著,像睡著了一般勺鸦。 火紅的嫁衣襯著肌膚如雪并巍。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,713評(píng)論 1 312
  • 那天换途,我揣著相機(jī)與錄音懊渡,去河邊找鬼。 笑死军拟,一個(gè)胖子當(dāng)著我的面吹牛剃执,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播懈息,決...
    沈念sama閱讀 41,170評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼肾档,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了漓拾?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,116評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤戒祠,失蹤者是張志新(化名)和其女友劉穎骇两,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體姜盈,經(jīng)...
    沈念sama閱讀 46,651評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡低千,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,714評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了馏颂。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片示血。...
    茶點(diǎn)故事閱讀 40,865評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖救拉,靈堂內(nèi)的尸體忽然破棺而出难审,到底是詐尸還是另有隱情,我是刑警寧澤亿絮,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布告喊,位于F島的核電站麸拄,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏黔姜。R本人自食惡果不足惜拢切,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,211評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望秆吵。 院中可真熱鬧淮椰,春花似錦、人聲如沸纳寂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽烈疚。三九已至黔牵,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間爷肝,已是汗流浹背猾浦。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留灯抛,地道東北人金赦。 一個(gè)月前我還...
    沈念sama閱讀 49,299評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像对嚼,于是被迫代替她去往敵國(guó)和親夹抗。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,870評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容