<轉(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)注!