
引子Shader聽起來好難啊嗨小朋友今天我們要聊一個聽起來很酷但很嚇人的東西——叫做Shader。Shader是什么——別慌——我用你能聽懂的話來說Shader——就是告訴電腦’這個東西看起來該長什么樣’的一張紙條打個比方你媽媽做菜要看菜譜**畫家畫畫要看參考圖****電腦畫3D畫面——也要看一張說明書這張說明書——就叫ShaderShader告訴電腦“這個球是紅色的”“這個盒子有花紋”“這個水面在波動”“這個火焰在閃爍”沒有Shader——電腦就不知道該怎么畫你的游戲畫面——屏幕上什么都沒有今天——我們就來看一份真實的Shader代碼——**一行一行拆開——用最簡單的話解釋——保證你能看懂一、準備工作先看整體骨架先來看看我們今天要研究的Shader——一個給物體染色 貼圖的簡單ShaderShader MyShader/Simple { Properties { _MainTex (貼圖, 2D) white {} _Color (顏色, Color) (1, 1, 1, 1) } SubShader { Tags { RenderType Opaque } Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include UnityCG.cginc sampler2D _MainTex; float4 _Color; struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; }; v2f vert(appdata v) { v2f o; o.pos UnityObjectToClipPos(v.vertex); o.uv v.uv; return o; } fixed4 frag(v2f i) : SV_Target { fixed4 col tex2D(_MainTex, i.uv); return col * _Color; } ENDCG } } FallBack Diffuse }看到這一堆代碼——你可能嚇一跳——“這什么鬼”**別怕——我們把它想象成一個魔法配方——一層一層拆開——你會發(fā)現(xiàn)它其實很簡單二、第一層這個魔法叫什么名字Shader MyShader/Simple這一行就像給你的魔法起名字**我這個Shader——名字叫MyShader/Simple就像你寫作業(yè)要寫上名字一樣**電腦要知道這是哪個Shader——才能用它想象一下媽媽的菜譜本子里——有一頁寫著紅燒肉的做法——這就是這道菜的名字。**這里的MyShader/Simple——就是我們這份Shader的名字。三、第二層可以調(diào)節(jié)的魔法材料Properties { _MainTex (貼圖, 2D) white {} _Color (顏色, Color) (1, 1, 1, 1) }這里叫Properties——中文是屬性——就是可以調(diào)節(jié)的材料**。想象你在做菜一份菜譜說“放多少鹽、放多少糖、放什么肉”**這些放多少、放什么**——就是可以調(diào)節(jié)的每次做菜可以調(diào)“今天多放點糖”、“今天用雞肉”Shader也一樣_MainTex——“用什么圖片當花紋”**就像用什么肉_Color——“要染成什么顏色”**就像放多少糖)具體來看每一行第一行_MainTex (貼圖, 2D) white {}**_MainTex——**這個材料的名字電腦內(nèi)部叫它這個**貼圖——**在Unity里顯示的名字給美術(shù)姐姐看的**2D——**說這是一張平面的圖片**white——**默認給一張白色的圖翻譯成人話**我需要一張圖片作為花紋——默認是白色的。第二行_Color (顏色, Color) (1, 1, 1, 1)**_Color——**顏色材料的名字**顏色——**給美術(shù)看的名字**Color——**類型是顏色**(1, 1, 1, 1)——**默認是白色 完全不透明這四個數(shù)字什么意思**第1個1——**紅色的多少1最紅**第2個1——**綠色的多少1最綠**第3個1——**藍色的多少1最藍**第4個1——**透明度1完全看得見0完全看不見當紅、綠、藍都是1**——混合出來就是白色**這就是Properties——就是可以調(diào)節(jié)的材料清單**。四、第三層具體的做法SubShaderSubShader { Tags { RenderType Opaque } // ... }**SubShader——就是具體的做法。為什么可以有好幾個SubShader——因為你家有個高級烤箱**——做法可以復(fù)雜點、味道更好你奶奶家只有普通鍋**——做法要簡單點、能做就行一份好的菜譜——要為不同的廚房準備不同做法Shader也一樣**好電腦——**用復(fù)雜的做法畫面漂亮**舊手機——**用簡單的做法能跑起來就行Tags給這份做法貼個標簽Tags { RenderType Opaque }**Tags——就是標簽。RenderType Opaque翻譯這個物體是不透明的Opaque的意思就是不透明**。這個標簽告訴電腦我不是玻璃、不是水——我是實心的東西**。電腦收到這個標簽就知道“哦那我按’畫不透明物體’的方式來處理你”。五、第四層真正的魔法配方PassPass { CGPROGRAM // ... 這里面就是真正的魔法 ENDCG }Pass——中文叫一遍——意思是畫一遍這個物體。為什么叫一遍——因為**有些復(fù)雜物體——要畫好幾遍才漂亮比如描邊效果——先畫一遍黑色的外殼——再畫一遍正常的物體**——就有描邊了**每一遍——就是一個Pass**我們這個簡單Shader——只有一個Pass——畫一遍就夠了。CGPROGRAM 和 ENDCG——“魔法開始和魔法結(jié)束”**CGPROGRAM——就像說魔法開始了**ENDCG——就像說魔法結(jié)束了**中間的所有東西——都是真正的魔法代碼。六、魔法內(nèi)部告訴電腦兩件事**打開魔法的盒子——里面有兩個最重要的魔法師#pragma vertex vert #pragma fragment frag這兩行是什么意思**vertex——**中文是頂點就是物體的一個個角**fragment——**中文是片段就是屏幕上的一個個小點#pragma vertex vert——頂點魔法師叫vert**#pragma fragment frag——片段魔法師叫frag**這兩個魔法師分工不同——我們分別來看看。七、魔法師1號頂點魔法師vertv2f vert(appdata v) { v2f o; o.pos UnityObjectToClipPos(v.vertex); o.uv v.uv; return o; }這個魔法師的工作——處理物體的每一個角。想象你的球——是由很多個三角形拼起來的**每個三角形有3個角**每個角——就是一個頂點頂點魔法師的任務(wù)把這些頂點從3D世界變到屏幕上****。打個比方你的球——在游戲世界里比如說位置(10, 5, 3)但屏幕是平的——只有左右和上下沒有前后必須有人把3D位置變成屏幕位置**——**這就是頂點魔法師的工作一行一行看v2f o;——創(chuàng)建一個結(jié)果盒子等下要把結(jié)果放進去o.pos UnityObjectToClipPos(v.vertex);——**這是最重要的一行翻譯**v.vertex——**這個頂點原本的位置游戲世界里的3D位置UnityObjectToClipPos(...)——一個神奇的轉(zhuǎn)換器——Unity幫我們寫好的函數(shù)它能把游戲世界的3D位置——變成屏幕上的2D位置就像有一臺神奇的相機——它站在你選的角度——咔嚓一下——把3D世界拍成一張平面照片o.uv v.uv;——把貼圖坐標傳下去uv是什么——它告訴電腦“這個頂點要貼上圖片的哪一部分”**。打個比方你有一張世界地圖****你的球體表面——**要貼上這張地圖球上的每個點——要對應(yīng)地圖上的某個位置**這個對應(yīng)關(guān)系——**就叫uv頂點魔法師說“記住這個uv等下畫顏色的時候用得到”return o;——把結(jié)果送出去頂點魔法師的工作到這里結(jié)束——它告訴電腦“這個頂點在屏幕上的位置在這里”“它要貼圖片的這一塊”八、魔法師2號片段魔法師fragfixed4 frag(v2f i) : SV_Target { fixed4 col tex2D(_MainTex, i.uv); return col * _Color; }片段魔法師的工作——處理屏幕上的每一個小點你的手機屏幕——其實是由很多很多小方塊組成的**每個小方塊——叫一個像素每一個像素——要有一個顏色片段魔法師——就是決定每個像素顏色的人一行一行看fixed4 col tex2D(_MainTex, i.uv);翻譯tex2D(...)——從圖片里取顏色的函數(shù)**_MainTex——**我們的貼圖還記得Properties里的嗎**i.uv——**頂點魔法師傳下來的貼圖坐標這行代碼在做什么看這個像素的uv坐標去貼圖上找對應(yīng)位置的顏色把那個顏色取出來存到col里打個比方你在一張世界地圖**上你想知道北京是什么顏色找到北京的uv坐標看地圖上那個位置——是紅色**好——**這里就是紅色fixed4是什么——它是一種能裝4個數(shù)字的盒子——裝 紅、綠、藍、透明度 四個值。return col * _Color;——返回最終顏色這一行——把兩個東西乘起來**col——**從貼圖取的顏色**_Color——**Properties里設(shè)的顏色為什么要乘起來打個比方貼圖是一張白色的貓咪**圖你在Properties里設(shè)了紅色**白色 × 紅色 紅色的貓咪相當于給貼圖染色**如果Properties設(shè)的是藍色**白色的貓咪 × 藍色 藍色的貓咪這就是染色的魔法——通過一個乘法就能讓一張圖變成各種顏色片段魔法師說“這個像素就用這個顏色”九、FallBack Diffuse——“備胎小哥”FallBack Diffuse這一行——是備胎小哥——Shader的救命機制**。它在說什么如果我這個Shader有些事情做不到**——請讓Diffuse這個Shader來幫我做。打個比方**你去學(xué)校演出——要跳舞但你不會翻跟頭**老師說沒事你不會翻跟頭的部分——讓李小明上來翻**李小明——就是你的備胎Shader也一樣**我們這個Shader——只會畫物體本身但游戲里還需要投射影子——我們沒寫這個功能FallBack Diffuse——就是說影子的部分讓Diffuse來幫我畫沒有這一行會怎樣——你的物體沒有影子**加上這一行——電腦會自動去Diffuse里借投影的代碼——你的物體就有影子了十、完整流程一個小球的魔法之旅讓我們把整個過程串起來——看看一個小球在游戲里是怎么被畫出來的第一步Unity打開你的Shader——看到名字MyShader/Simple——“好我要用這個”第二步讀取Properties看看用戶在Material里設(shè)了什么哦貼圖是蘋果.jpg顏色是紅色(1, 0, 0, 1)第三步頂點魔法師上場拿到小球的每一個頂點算出它們在屏幕上的位置傳遞uv坐標第四步片段魔法師上場對屏幕上小球覆蓋的每一個像素**從蘋果.jpg里取對應(yīng)的顏色和紅色相乘得到最終顏色第五步畫到屏幕上第六步如果需要影子用Diffuse的投影代碼畫影子**你的紅色蘋果小球——就出現(xiàn)在屏幕上了十一、舉一反三小小的變化能玩出很多花樣**理解了這個基礎(chǔ)Shader——我們能玩很多花樣花樣1讓它變亮把最后一行改成return col * _Color * 2; // 乘以2結(jié)果顏色變成兩倍亮——物體像發(fā)光一樣花樣2讓它閃爍改成return col * _Color * (sin(_Time.y) 1); // 用時間的sin值_Time是Unity自動給你的時間變量——sin函數(shù)會不斷變化——物體就會呼吸一樣閃爍**花樣3讓它反色改成return 1 - col * _Color; // 用1減去顏色結(jié)果紅色變成青色、黑色變成白色——像照片的負片**看到了嗎——Shader其實是給電腦寫數(shù)學(xué)**——**只要你會一點數(shù)學(xué)——就能創(chuàng)造各種視覺效果十二、總結(jié)Shader沒那么難**回頭看看——我們剛才做了什么Shader 告訴電腦怎么畫東西的配方Properties “可以調(diào)節(jié)的材料”SubShader “具體的做法”Pass “畫一遍”頂點魔法師 “算出每個角在屏幕上的位置”片段魔法師 “算出每個像素的顏色”FallBack “備胎小哥我不會的它幫我”Shader不是神秘的黑魔法——它是一份寫給電腦的說明書**每一行代碼——都在說電腦你這樣做“用這張貼圖”“和這個顏色相乘”“變成屏幕位置”“輸出到屏幕上”就這么簡單結(jié)語一份魔法配方的溫暖從一個看起來嚇人的Shader代碼到把它拆解成配方、“魔法師”、備胎小哥的小學(xué)生語言到看清楚每一行在做什么事情——Shader其實是這樣一個東西它是給電腦的畫畫說明書——告訴它這個物體長什么樣**Properties——“可以調(diào)節(jié)的顏料”SubShader——“具體的畫法”Pass——“畫一遍的動作”頂點魔法師——“把3D變到屏幕”片段魔法師——“給每個像素上色”FallBack——“我不會的讓別人幫我”它像一個精心設(shè)計的畫畫流程每一步都清晰每一步都有意義每一步都在為最終畫面服務(wù)它有小小的Properties——讓美術(shù)姐姐能調(diào)顏色調(diào)貼圖。它有魔法師的分工——頂點管位置、片段管顏色。它有貼心的FallBack——不會的事情有人幫忙。它有清晰的結(jié)構(gòu)——從名字到屬性到畫法一目了然。下次當你打開一個Shader文件——請記得那看起來密密麻麻的代碼——其實是一份給電腦的說明書**一份畫畫的配方**一份每一行都在做一件具體事情**的清單每一行float4、return、tex2D——都不神秘——都在說一件很具體的小事。每一個Property——都是一個可以調(diào)節(jié)的東西。每一個Pass——都是畫一遍的動作。每一個函數(shù)——都是魔法師的一個小工作。Shader不是天才才能寫的——是每個愿意慢慢理解的小朋友都能學(xué)會的每一次你多懂一行代碼——就多掌握一點給電腦畫畫的能力。每一次你改動一個數(shù)字——就在創(chuàng)造屬于自己的視覺魔法**。每一次你寫出一個Shader——都是在和電腦聊天**——告訴它畫一張這樣的畫。這——就是Shader的真相——不是高深難懂的黑魔法——而是一份寫給電腦的、可以慢慢學(xué)、慢慢懂、慢慢玩的畫畫說明書——是每一位小朋友、每一位新手、每一位好奇的開發(fā)者都能一步步掌握的友好的魔法。它不神秘——每一行都能拆開解釋。它不復(fù)雜——歸根到底就是位置顏色兩件事。它不遙遠——**只要愿意——從今天就能開始學(xué)。在這份魔法配方的背后是電腦圖形學(xué)的扎實基礎(chǔ)**是Unity為你提供的貼心工具**是等待你去探索的創(chuàng)造力空間**是從看不懂到能自己寫的成長路徑**這——就是Shader——一份等你去理解、去玩耍、去創(chuàng)造的友好的魔法**。每一位曾經(jīng)看代碼就頭疼的小朋友——只要愿意慢慢拆開——都能像今天這樣看懂了。每一份看似復(fù)雜的Shader——歸根到底就是我們今天講的這些簡單的道理**。每一位懷著好奇心的學(xué)習(xí)者——都能在Shader的世界里找到自己的樂趣**。這——就是Shader送給每一位小小學(xué)習(xí)者的、最溫柔也最鼓舞人心的友好禮物。 ?