作為一個(gè)系統(tǒng)介紹Unity3D中Shader編寫(xiě)的系列文章的開(kāi)篇,本文的第一部分為系列文章的前言峭拘,然后第二部分介紹了這個(gè)系列文章中我們會(huì)使用的游戲場(chǎng)景創(chuàng)建方式俊庇,最后一部分講解了如何在Unity中創(chuàng)建和使用Shader,為后面專(zhuān)注于介紹如何在Unity中進(jìn)行Shader編程打好了基礎(chǔ)鸡挠。
因?yàn)楹竺嫱瞥龅南盗形恼聲?huì)著重介紹各種Shader的寫(xiě)法和實(shí)現(xiàn)辉饱,不會(huì)再具體講解如何創(chuàng)建場(chǎng)景和寫(xiě)出Shader代碼后如何使用,相信這篇文章作為本系列的開(kāi)篇拣展,發(fā)表出來(lái)肯定還是會(huì)對(duì)大家多少有些幫助的彭沼。大家以后閱讀稍后推出的Unity Shader系列文章的時(shí)候,有場(chǎng)景創(chuàng)建或者Shader代碼寫(xiě)好了如何使用方面的疑問(wèn)的話(huà)备埃,可以隨時(shí)回過(guò)頭來(lái)查閱這篇文章姓惑。
OK,就讓我們從這篇文章開(kāi)始一趟精彩萬(wàn)分的Shader游戲編程旅途按脚。
依舊國(guó)際慣例于毙,看幾張文章中實(shí)現(xiàn)的場(chǎng)景美圖先:
上圖中展示的文本配套Unity工程的可運(yùn)行exe淺墨也為大家準(zhǔn)備好了节沦,有興趣的朋友們可以點(diǎn)擊 這里進(jìn)行下載兜喻、運(yùn)行和探索:
PS:文章配套的三個(gè)unitypackage和最終的工程源碼在文章末尾處提供下載。
一忿檩、系列文章前言
在這個(gè)系列開(kāi)頭,淺墨想說(shuō)的是介蛉,其實(shí)這個(gè)系列文章中我們學(xué)的主要是著色器編程技術(shù)萌庆,重點(diǎn)不是學(xué)Unity。
甚至可以這樣說(shuō)币旧,我們學(xué)的是HLSL——沒(méi)錯(cuò)践险,就是DirectX中的那個(gè)HLSL。
為什么這樣講佳恬,讓我們來(lái)捋一捋捏境。
首先于游,Unity中編寫(xiě)Shader的語(yǔ)言叫做ShaderLab毁葱,而ShaderLab說(shuō)白了就是裹著一層皮的CG著色器語(yǔ)言而已。Cg贰剥,即C forgraphics倾剿,即用于圖形的C語(yǔ)言,是微軟Microsoft和英偉達(dá)NVIDIA相互協(xié)作在標(biāo)準(zhǔn)硬件光照語(yǔ)言的語(yǔ)法和語(yǔ)義上達(dá)成的一種一致性協(xié)議蚌成。
HLSL和CG其實(shí)是同一種語(yǔ)言(參見(jiàn)Cg教程_可編程實(shí)時(shí)圖形權(quán)威指南29頁(yè)的致謝部分)前痘。很多時(shí)候,我們會(huì)發(fā)現(xiàn)用HLSL寫(xiě)的代碼可以直接當(dāng)中Cg代碼使用担忧。
Microsoft和NVIDIA聯(lián)手推出CG語(yǔ)言芹缔,想在經(jīng)濟(jì)和技術(shù)上實(shí)現(xiàn)雙贏(yíng),從而通過(guò)這種方式聯(lián)手打擊他們共同的對(duì)手GLSL瓶盛。
既然Unity主打Shader編程的語(yǔ)言ShaderLab是CG語(yǔ)言披上一層皮最欠,而CG語(yǔ)言又約等于HLSL。這就是說(shuō)惩猫,在Unity中寫(xiě)Shader約等于用HLSL寫(xiě)Shader芝硬,也就約等于給DirectX寫(xiě)Shader。雖然有點(diǎn)繞orz轧房,最后總結(jié)一下也就是:
在Unity中寫(xiě)Shader約等于給DirectX寫(xiě)Shader
而Unity又是這樣一個(gè)集萬(wàn)千寵愛(ài)于一身的可見(jiàn)即所得的目前在移動(dòng)互聯(lián)網(wǎng)時(shí)代火到不行的游戲引擎拌阴。可以說(shuō)奶镶,Unity可見(jiàn)即所得的開(kāi)發(fā)環(huán)境非常適合Shader的學(xué)習(xí)迟赃,而且在Unity分分鐘可以創(chuàng)建出來(lái)一個(gè)漂亮的場(chǎng)景里面寫(xiě)寫(xiě)Shader,心情都會(huì)好很多厂镇。不再是苦逼地面朝代碼背朝天了纤壁。
所以淺墨決定開(kāi)始在Unity中進(jìn)行這個(gè)shader學(xué)習(xí)系列,畢竟Unity剪撬、CG摄乒、HLSL不分家。
另外促成這個(gè)系列文章的成型的一個(gè)原因是Unity的Asset store。淺墨很喜歡Unity引領(lǐng)的Asset store這樣一站式的游戲素材商店馍佑,里面有數(shù)不盡的游戲插件斋否、素材、資源拭荤、腳本代碼參考茵臭、shader資料等等,甚至現(xiàn)成的游戲工程范例舅世。Asset store給開(kāi)發(fā)者們帶來(lái)了很大的便利旦委,簡(jiǎn)直就是游戲開(kāi)發(fā)界的大寶庫(kù)。
但需要說(shuō)明的是雏亚,Unity這款引擎的缺點(diǎn)也是很明顯的缨硝。比如Unity不對(duì)外開(kāi)源,我們看不到源碼罢低,坑比較多查辩,遇到坑了看不到
源碼給解決帶來(lái)了很大難度。執(zhí)行效率還是不太行网持,渲染大場(chǎng)景和做高級(jí)渲染的時(shí)候還是顯得力不從心宜岛。比如渲染大片的近乎
真實(shí)的動(dòng)態(tài)水面時(shí),幀數(shù)就立馬降下來(lái)了功舀,需要后期做很多的優(yōu)化萍倡。就畫(huà)面質(zhì)量來(lái)說(shuō)和Unreal Engine和cryEngine等次時(shí)代
引擎比還是有差距。是最近幾年互聯(lián)網(wǎng)的浪潮和得屌絲者的天下商業(yè)模式促進(jìn)了其最近幾年如此的成功辟汰。
說(shuō)了這么多列敲,總結(jié)一下。
Unity只是我們學(xué)習(xí)CG莉擒、HLSL編程可見(jiàn)即所得的好幫手好工具而已酿炸。我們主要還是利用它來(lái)更好的學(xué)計(jì)算機(jī)圖形學(xué)和Shader編程,順便掌握目前熱門(mén)的Unity引擎的基本使用和研發(fā)思路涨冀。
我們還是忘不了C++和DirectX填硕,我們還是渴望通過(guò)自己的努力,最終有能力用C/C++一句一句寫(xiě)出自己的游戲引擎鹿鳖,我們還是想從零開(kāi)始造輪子扁眯,畢竟那樣一句一句寫(xiě)出來(lái)的代碼都是自己的,而不是那些別人為我們準(zhǔn)備好的現(xiàn)成的API函數(shù)翅帜。
謹(jǐn)以此前言姻檀,與諸君共勉。
二涝滴、用Unity創(chuàng)建第一個(gè)美麗的游戲場(chǎng)景
首先要說(shuō)明的是绣版,作為一個(gè)從入門(mén)內(nèi)容開(kāi)始逐漸深入介紹的系列教程胶台,這一部分在Unity下創(chuàng)建場(chǎng)景的內(nèi)容是為還不太熟悉Unity的朋友們準(zhǔn)備的,如果你已經(jīng)熟悉了Unity的使用杂抽,這部分可以快速跳讀诈唬。
OK,正式開(kāi)始吧缩麸。
2.1 【第一步】當(dāng)然是新建一個(gè)項(xiàng)目
大家肯定都知道铸磅,每次新建項(xiàng)目后或者新建場(chǎng)景后,會(huì)得到一片Unity中默認(rèn)為全藍(lán)的場(chǎng)景杭朱。想要場(chǎng)景變得有生機(jī)阅仔,一般都是去菜單欄的GameObject里面新建Terrain(地形)然后進(jìn)行編輯。Terrain的制作很耗時(shí)而且水很深弧械,想要精通也是得花一些功夫八酒。
甚至Unity Asset Store中有各種可以輔助快速生成AAA級(jí)大作風(fēng)格的真實(shí)場(chǎng)景的插件,如Terrain Composer梦谜,配合著Unity中有名的Relief Terrain Pack v3地形輔助著色渲染工具丘跌,可以生成近乎以假亂真的三維自然風(fēng)光出來(lái)。
漂亮地形的創(chuàng)建當(dāng)然不屬于我們講解的重點(diǎn)唁桩,網(wǎng)絡(luò)上有數(shù)不清的文章和視頻講這方面的內(nèi)容,有需要的話(huà)耸棒,大家去學(xué)一些基礎(chǔ)荒澡,或者直接用Asset Store中現(xiàn)成的各種漂亮場(chǎng)景。反正淺墨是各種在網(wǎng)上下載AssetStore中美工大牛們做場(chǎng)景与殃,然后簡(jiǎn)單的修改单山,為自己測(cè)試和平常調(diào)數(shù)值所用。
這不幅疼,淺墨為了寫(xiě)這篇博客米奸,就為大家再加工“創(chuàng)作“了一個(gè)夏威夷風(fēng)格的場(chǎng)景來(lái):)
2.2 【第二步】導(dǎo)入Hawaii Environment.unitypackage場(chǎng)景包
上文講到,為了節(jié)約時(shí)間爽篷,淺墨提前為大家修改好了一個(gè)場(chǎng)景悴晰,然后這個(gè)場(chǎng)景已經(jīng)打包,叫做“HawaiiEnvironment.unitypackage “逐工,在文章末尾提供下載铡溪。(限于Unity對(duì)中文的支持拙計(jì),無(wú)奈只能取英文名泪喊,不然直接導(dǎo)入就報(bào)錯(cuò))棕硫。
雙擊這個(gè)包,導(dǎo)入到我們空空如也的工程中袒啼,經(jīng)過(guò)一段時(shí)間的讀條哈扮,就導(dǎo)入完畢了纬纪。然后我們雙擊打開(kāi)出現(xiàn)在Project面板中Assets文件夾下的場(chǎng)景文件Hawaii Environment.unity
接著便打開(kāi)了場(chǎng)景,如果打開(kāi)成功滑肉,Scene面板中應(yīng)該便出現(xiàn)了如下類(lèi)似的場(chǎng)景畫(huà)面:
因?yàn)槁匀チ藞?chǎng)景編輯部分育八,直接導(dǎo)入,所以過(guò)程是非常簡(jiǎn)單的赦邻。但是髓棋,這還完全不夠。讓我們?cè)趫?chǎng)景中添加一個(gè)可以自由控制的攝像機(jī)吧惶洲。
2.3 【第三步】添加第一人稱(chēng)攝像機(jī)
淺墨準(zhǔn)備的這個(gè)場(chǎng)景包是沒(méi)有攝像機(jī)的按声,單單就是場(chǎng)景,所以我們還需要在場(chǎng)景中添加一個(gè)攝像機(jī)恬吕。
大家應(yīng)該清楚签则,比較常見(jiàn)添加攝像機(jī)的做法是通過(guò)菜單欄中的GameObject->CreateOther->Camera來(lái)添加。但這種方式的攝像機(jī)是固定的铐料,不合我們的要求渐裂。我們想添加的是一個(gè)在游戲運(yùn)行時(shí)可以自由移動(dòng)視角的第一人稱(chēng)攝像機(jī)。其實(shí)Unity自帶的資源包中剛好可以滿(mǎn)足我們的要求钠惩。于是我們?cè)赑roject面板中右鍵【Import Package】柒凉,或者菜單欄中Assets->ImportPackage->Character Controller來(lái)導(dǎo)入官網(wǎng)為我們準(zhǔn)備的的角色控制資源包。如下圖:
點(diǎn)擊之后篓跛,會(huì)彈出如下的資源導(dǎo)入確認(rèn)窗口膝捞,我們直接點(diǎn)確定就行了:
因?yàn)檫@個(gè)包很小,所以很快就導(dǎo)入完成愧沟,這時(shí)Assets根文件夾下會(huì)出現(xiàn)一個(gè)名為Standard Assets的文件夾蔬咬,展開(kāi)它或者點(diǎn)進(jìn)去,就是名為【CharacterControllers】的文件夾沐寺,繼續(xù)點(diǎn)進(jìn)去林艘,發(fā)現(xiàn)了一個(gè)膠囊狀的叫【First PersonController】的家伙,這就是我們需要的了混坞。
然后我們先在Scene面板中利用【右鍵+鍵盤(pán)W狐援、A、S拔第、D】以及滾輪等操作調(diào)整好場(chǎng)景咕村,然后在我們剛才的【CharacterControllers】下點(diǎn)擊這個(gè)膠囊裝的【First Person Controller】按住不放,拖動(dòng)到Scene場(chǎng)景中蚊俺,選到合適的地方(如草坪上)后就放手懈涛,操作如下:
Unity會(huì)自動(dòng)將這個(gè)【CharacterControllers】的中心位置依附到地形表面。
這個(gè)時(shí)候我們會(huì)發(fā)現(xiàn)之前是黑屏的Game面板中也有了畫(huà)面:
這時(shí)我們還要將這個(gè) First Person Controller的底部向上拖動(dòng)一點(diǎn)泳猬,不然運(yùn)行游戲時(shí)我們會(huì)不停的往下掉批钠,因?yàn)樵赨nity默認(rèn)情況下First Person Controller的中心位于中部宇植,這會(huì)照成它的底部已經(jīng)穿透地形,懸空位于地形的下方埋心,自然一運(yùn)行就往下掉指郁。
我們Hierarchy面板中選中First Person Controller,工具欄中選擇【移動(dòng)】工具然后對(duì)著場(chǎng)景中膠囊上的代表Y軸的綠色箭頭向上適當(dāng)拖動(dòng)拷呆,讓膠囊的底部確保位于草地之上就行了闲坎。
這時(shí)候我們點(diǎn)擊unity中間三角尖的【運(yùn)行】按鈕,就可以自由地在場(chǎng)景中觀(guān)察和移動(dòng)了~
Unity第一人稱(chēng)控制器默認(rèn)操作方式類(lèi)似CS茬斧,CF一類(lèi)的FPS游戲腰懂。W、A项秉、S绣溜、D前后左右移動(dòng),空格跳躍娄蔼,鼠標(biāo)移動(dòng)調(diào)整視角怖喻,非常有親切感有木有~
這就很好地體現(xiàn)了Unity的入門(mén)容易的特點(diǎn),只用點(diǎn)點(diǎn)鼠標(biāo)一個(gè)漂亮的場(chǎng)景就展現(xiàn)在眼前岁诉。
2.4 【第四步】在游戲場(chǎng)景中加入背景音樂(lè)
話(huà)說(shuō)這么美麗的場(chǎng)景怎么能沒(méi)有音樂(lè)锚沸?
不妨就讓我們添加一段優(yōu)美的鋼琴曲吧。曲子淺墨都為大家準(zhǔn)備好了唉侄,上文已經(jīng)導(dǎo)入的HawaiiEnvironment.unitypackage包中咒吐,在A(yíng)ssets根目錄下包含了一首林海的《日光告別》。
我們要做的只要是把這個(gè)音樂(lè)文件拖拽到第一人稱(chēng)攝像機(jī)First PersonController上就可以了属划,就像箭頭中指的這樣:
拖拽完成后,F(xiàn)irst Person Controller的Inspector面板中就應(yīng)該會(huì)多了一個(gè)Audio Source組件候生。
運(yùn)行場(chǎng)景同眯,伴隨著美麗的場(chǎng)景,“吹著海風(fēng)”唯鸭,優(yōu)美的鋼琴曲入耳须蜗,非常怡人。
先放兩張測(cè)試過(guò)程中的場(chǎng)景美圖目溉,再繼續(xù)我們下一部分的講解吧:
非常逼真的水效明肮,采用大名鼎鼎的NGUI工作室Tasharen Entertainment出品的水面插件:
三、導(dǎo)入QianMo’s Toolkit并使用
3.1 認(rèn)識(shí)QianMo's Toolkit
所謂的QianMo's Toolkit缭付,其實(shí)就是淺墨為場(chǎng)景測(cè)試寫(xiě)的一個(gè)小腳本工具集柿估,打包成一個(gè)unitypackage方便多次使用而已。若有需要陷猫,淺墨會(huì)在其中添加更多的功能秫舌。
以后我們每次新建工程的時(shí)候的妖,只要導(dǎo)入這個(gè)小工具就可以使用我們之前已經(jīng)寫(xiě)好的各種特性,非常便捷足陨。
【QianMo's Toolkit v1.0.unitypackage單獨(dú)下載請(qǐng)點(diǎn)我】
QianMo's Toolkit v1.0版的內(nèi)容如下:
也就是包含了五個(gè)腳本文件嫂粟,兩張圖片。這五個(gè)腳本文件的功能分別為:
ShowFPS:在游戲運(yùn)行時(shí)顯示幀率相關(guān)信息
ShowObjectInfo:在場(chǎng)景中和游戲窗口中分別顯示添加給任意物體文字標(biāo)簽信息墨缘。隱藏和顯示可選星虹,基于公告板技術(shù)實(shí)現(xiàn)。
ShowGameInfo:在游戲運(yùn)行時(shí)顯示GUI相關(guān)說(shuō)明
ShowLogo:在游戲運(yùn)行時(shí)顯示Logo
ShowUI:在游戲運(yùn)行時(shí)顯示簡(jiǎn)單的鑲邊UI镊讼。
這個(gè)五個(gè)腳本的代碼淺墨都已經(jīng)詳細(xì)注釋?zhuān)诤罄m(xù)文章中有機(jī)會(huì)我們會(huì)介紹其具體寫(xiě)法宽涌。這篇文章中就先簡(jiǎn)單的認(rèn)識(shí)一下他們就好。PS:下文第四節(jié)中貼出了ShowGameInfo腳本的全部代碼狠毯。
3.2 使用QianMo's Toolkit
上文已經(jīng)說(shuō)了护糖,既然這是一個(gè)unitypackage,那么只用雙擊它導(dǎo)入到我們當(dāng)前的項(xiàng)目中就行了嚼松。導(dǎo)入完成之后嫡良。Assets文件夾下就又多了一個(gè)名為” QianMo's Toolkit v1.0“的文件夾,內(nèi)容就是我們剛才介紹的5個(gè)腳本文件兩張圖:
暫時(shí)我們要使用的是ShowGameInfo献酗、ShowLogo寝受、ShowUI這三個(gè)腳本文件,把它們一起拖到我們之前創(chuàng)建的第一人稱(chēng)攝像機(jī)First Person Controller上就行了:
拖動(dòng)完成后罕偎,我們?cè)贔irst Person Controller的Inspector面板中發(fā)現(xiàn)其多了三個(gè)組件很澄,就是我們給他添加的這個(gè)三個(gè)腳本:
其實(shí)Show Logo無(wú)關(guān)緊要,就是顯示了淺墨自己的logo而已,當(dāng)然你可以換成自己的logo颜及。show UI也無(wú)關(guān)緊要甩苛,就是顯示了一個(gè)頂部的鑲邊png。主要的是這個(gè)ShowGameInfo俏站,它是用于顯示幀率等相關(guān)文字消息的讯蒲。(其實(shí)簡(jiǎn)約黨會(huì)覺(jué)得三個(gè)都無(wú)關(guān)緊要,orz)
拖動(dòng)完成之后肄扎,再次運(yùn)行墨林,我們來(lái)看一看效果:
可以發(fā)現(xiàn),游戲窗口的邊邊角角多了一些說(shuō)明和圖片犯祠,以及有了幀率的顯示旭等。
四、書(shū)寫(xiě)和使用第一個(gè)Shader
上文講解的都是一般的場(chǎng)景構(gòu)建過(guò)程衡载,接下來(lái)就要正式開(kāi)始我們的核心部分搔耕,書(shū)寫(xiě)第一個(gè)Shader了。在完成上文中講解的創(chuàng)建好漂亮的場(chǎng)景之后月劈,我們首先可以在A(yíng)ssets根目錄下創(chuàng)建一個(gè)文件夾度迂,用于以后Shader和Material文件的存放藤乙。創(chuàng)建過(guò)程可以是在Project面板的空白處右鍵->Create->Folder,也可以是點(diǎn)擊Project面板中的Create下拉菜單->Folder
給新出來(lái)的這個(gè)文件夾取名Shaders惭墓,然后回車(chē)坛梁。然后再用同樣的方法在A(yíng)ssets根目錄下創(chuàng)建一個(gè)名為T(mén)extures的文件夾,用于稍后素材圖片的存放腊凶。那么划咐,如果你按照淺墨按照目前描述的步驟來(lái)的話(huà),Assets根目錄到現(xiàn)在就是這樣的5個(gè)文件夾:
接著钧萍,進(jìn)去到我們創(chuàng)建的Shader文件夾褐缠。同樣在空白處右鍵->Create->Shader,或者是直接點(diǎn)Create下拉菜單->Shader风瘦,創(chuàng)建一個(gè)Shader文件队魏,取名為 “0.TheFirstShader”。然后雙擊打開(kāi)它万搔,Unity會(huì)默認(rèn)使用名為MonoDevelop的編輯器打開(kāi)這個(gè)Shader文件胡桨。
小tips:可以在菜單欄中Edit->Preferences->ExternalTools中調(diào)成默認(rèn)用Visual Studio打開(kāi),但未經(jīng)修改配置文件的Visual Studio對(duì)Shader后綴的文件是不支持語(yǔ)法高亮的瞬雹,淺墨修改了部分配置文件才讓Visual Studio支持了Unity Shader書(shū)寫(xiě)的語(yǔ)法高亮昧谊。對(duì)于不太清楚如何修改的朋友,可以善用搜索引擎酗捌,或者過(guò)些天淺墨會(huì)單獨(dú)發(fā)一篇名為《Unity中使用Visual Studio編寫(xiě)shader并設(shè)置代碼高亮》的文章來(lái)專(zhuān)門(mén)講解呢诬。
作為初次寫(xiě)Shader,我們暫且先用MonoDevelop頂一頂胖缤,后面的文章再換用修改了配置文件的Visual Studio尚镰。
好了,用MonoDevelop打開(kāi)我們新建的這個(gè)Shader文件哪廓,發(fā)現(xiàn)Unity已經(jīng)為我們寫(xiě)好了很多代碼钓猬。
我們不妨自己重新寫(xiě)點(diǎn)不一樣的東西。刪掉原本的這些代碼撩独,拷貝淺墨寫(xiě)的如下代碼到編輯器中:
//-----------------------------------------------【Shader說(shuō)明】----------------------------------------------
// Shader功能: 凹凸紋理顯示+自選邊緣顏色和強(qiáng)度
// 使用語(yǔ)言: Shaderlab
// 開(kāi)發(fā)所用IDE版本:Unity4.5 06f 、Monodevelop
// 2014年11月2日 Created by 淺墨
// 更多內(nèi)容或交流請(qǐng)?jiān)L問(wèn)淺墨的博客:http://blog.csdn.net/poem_qianmo
//---------------------------------------------------------------------------------------------------------------------
Shader "淺墨Shader編程/0.TheFirstShader"
{
//-------------------------------【屬性】-----------------------------------------
Properties
{
_MainTex ("【紋理】Texture", 2D) = "white" {}
_BumpMap ("【凹凸紋理】Bumpmap", 2D) = "bump" {}
_RimColor ("【邊緣顏色】Rim Color", Color) = (0.17,0.36,0.81,0.0)
_RimPower ("【邊緣顏色強(qiáng)度】Rim Power", Range(0.6,9.0)) = 1.0
}
//----------------------------【開(kāi)始一個(gè)子著色器】---------------------------
SubShader
{
//渲染類(lèi)型為Opaque账月,不透明
Tags { "RenderType" = "Opaque" }
//-------------------開(kāi)始CG著色器編程語(yǔ)言段-----------------
CGPROGRAM
//使用蘭伯特光照模式
#pragma surface surf Lambert
//輸入結(jié)構(gòu)
struct Input
{
float2 uv_MainTex;//紋理貼圖
float2 uv_BumpMap;//法線(xiàn)貼圖
float3 viewDir;//觀(guān)察方向
};
//變量聲明
sampler2D _MainTex;//主紋理
sampler2D _BumpMap;//凹凸紋理
float4 _RimColor;//邊緣顏色
float _RimPower;//邊緣顏色強(qiáng)度
//表面著色函數(shù)的編寫(xiě)
void surf (Input IN, inout SurfaceOutput o)
{
//表面反射顏色為紋理顏色
o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
//表面法線(xiàn)為凹凸紋理的顏色
o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
//邊緣顏色
half rim = 1.0 - saturate(dot (normalize(IN.viewDir), o.Normal));
//邊緣顏色強(qiáng)度
o.Emission = _RimColor.rgb * pow (rim, _RimPower);
}
//-------------------結(jié)束CG著色器編程語(yǔ)言段------------------
ENDCG
}
//“備胎”為普通漫反射
Fallback "Diffuse"
}
由于這是第一篇Shader系列文章综膀,已經(jīng)涉及到很多內(nèi)容了,所以淺墨不可能展開(kāi)講解這段代碼的具體思路和寫(xiě)法局齿,不過(guò)已經(jīng)詳細(xì)注釋?zhuān)蠹覒?yīng)該會(huì)多少有點(diǎn)明白剧劝。隨著稍后文章的深入,這段代碼就顯得很簡(jiǎn)單易懂了抓歼。
拷貝完成讥此,保存一下這段代碼拢锹,unity會(huì)自動(dòng)檢測(cè)和編譯被保存的代碼,我只需返回Unity窗口萄喳,等待編譯完成即可卒稳。若沒(méi)有錯(cuò)誤,在“0.TheFirstShader”的inspector面板中得到的結(jié)果應(yīng)該是有紅色的錯(cuò)誤提示的他巨。
需要注意的是充坑,Shader想要使用到游戲物體上,一般得有個(gè)媒介染突,這個(gè)媒介就是我們的老朋友——材質(zhì)(Material)捻爷。我們把Shader作用于材質(zhì),接著再把材質(zhì)對(duì)應(yīng)地作用于給游戲物體份企,這樣寫(xiě)的Shader就間接地給物體表面使用了也榄。
而這一層關(guān)系,在Unity中完全可以通過(guò)點(diǎn)點(diǎn)鼠標(biāo)司志,拖動(dòng)來(lái)完成甜紫。下面我們就來(lái)講一講如何將一個(gè)著色程序的結(jié)果顯示到物體表面上。
知道以上原理了就很簡(jiǎn)單了俐芯,在“0.TheFirstShader.shader”的同一目錄下創(chuàng)建一個(gè)Material棵介。同樣是可以通過(guò)Create下拉菜單->Material或者空白處右鍵->create->Material來(lái)完成。
為了到時(shí)候方便對(duì)應(yīng)吧史,我們將這個(gè)材質(zhì)也取名為0.TheFirstShader邮辽。
接著,將0.TheFirstShader.shader拖動(dòng)到0.TheFirstShader材質(zhì)身上然后釋放贸营。
拖動(dòng)完成后吨述,我們單擊0.TheFirstShader材質(zhì),打開(kāi)他的面板钞脂,發(fā)現(xiàn)他已經(jīng)和一開(kāi)始不一樣了揣云,泛著藍(lán)光:
還沒(méi)完,接下來(lái)我們還得給這個(gè)材質(zhì)添加兩張紋理圖片冰啃。圖片淺墨也已經(jīng)提前準(zhǔn)備好了邓夕,在名為T(mén)extures01 by QianMo.unitypackage的Unity包中,同樣雙擊這個(gè)包然后打開(kāi)導(dǎo)入到項(xiàng)目中阎毅。
【Textures01 by QianMo.unitypackage單獨(dú)下載請(qǐng)點(diǎn)我】
我們?cè)赥extures文件夾下找到這兩張紋理焚刚,接下來(lái)做的就是將他們拖動(dòng)到0.TheFirstShader材質(zhì)對(duì)應(yīng)的紋理區(qū)域中,如下:
或者點(diǎn)擊這里的Select分別選擇扇调,操作如下:
兩張紋理選擇完畢后矿咕,我們的材質(zhì)就準(zhǔn)備好了,最后的結(jié)果,有點(diǎn)黑科技碳柱,如各種科幻游戲和電影中發(fā)光的礦石捡絮,非常炫酷:
OK,那么就只剩下最后一步了莲镣,就是在場(chǎng)景中創(chuàng)建一個(gè)物體福稳,然后將我們做好的材質(zhì)拖拽到物體身上賦給這個(gè)物體就行了。
菜單欄【GameObject】->【Create Other】->【Capsule】或者【Create】下拉菜單->【Capsule】來(lái)在場(chǎng)景中創(chuàng)建一個(gè)膠囊裝的物體剥悟。把他拖動(dòng)到和我們的第一人稱(chēng)攝像機(jī)【First Person Controller】很近的地方灵寺,這樣方便觀(guān)察,接著就可以把我們的“0.TheFirstShader”材質(zhì)直接拖拽給場(chǎng)景中的這個(gè)膠囊区岗,或者Hierachy面板中【Capsule】名字上就行了略板,操作如下圖中的箭頭所示:
經(jīng)過(guò)拖拽,Capsule加上Material后的效果如下:
4.1 給使用Shader的物體加上文字說(shuō)明
為了以后介紹多個(gè)Shader寫(xiě)法時(shí)能更清晰更方便慈缔,淺墨專(zhuān)門(mén)在QianMo’s Toolkit中做了一個(gè)可以在場(chǎng)景中和游戲窗口中分別顯示附加給任意物體文字標(biāo)簽信息的工具腳本叮称,叫做ShowObjectInfo,其詳細(xì)注釋的代碼如下:
//-----------------------------------------------【腳本說(shuō)明】-------------------------------------------------------
// 腳本功能: 在場(chǎng)景中和游戲窗口中分別顯示給任意物體附加的文字標(biāo)簽信息
// 使用語(yǔ)言: C#
// 開(kāi)發(fā)所用IDE版本:Unity4.5 06f 藐鹤、Visual Studio 2010
// 2014年10月 Created by 淺墨
// 更多內(nèi)容或交流瓤檐,請(qǐng)?jiān)L問(wèn)淺墨的博客:http://blog.csdn.net/poem_qianmo
//---------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------【使用方法】-------------------------------------------------------
// 第一步:在Unity中拖拽此腳本到某物體之上,或在Inspector中[Add Component]->[淺墨's Toolkit v1.0]->[ShowObjectInfo]
// 第二步:在Inspector里,Show Object Info 欄中的TargetCamera參數(shù)中選擇需面向的攝像機(jī),如MainCamera
// 第三步:在text參數(shù)里填需要顯示輸出的文字娱节。
// 第四步:完成挠蛉。運(yùn)行游戲或在場(chǎng)景編輯器Scene中查看顯示效果。
// PS:默認(rèn)情況下文本信息僅在游戲運(yùn)行時(shí)顯示肄满。
// 若需要在場(chǎng)景編輯時(shí)在Scene中顯示谴古,請(qǐng)勾選Show Object Info 欄中的[Show Info In Scene Editor]參數(shù)。
// 同理,勾選[Show Info In Game Play]參數(shù)也可以控制是否在游戲運(yùn)行時(shí)顯示文本信息
//---------------------------------------------------------------------------------------------------------------------
//預(yù)編譯指令稠歉,檢測(cè)到UNITY_EDITOR的定義掰担,則編譯后續(xù)代碼
#if UNITY_EDITOR
//------------------------------------------【命名空間包含部分】----------------------------------------------------
// 說(shuō)明:命名空間包含
//----------------------------------------------------------------------------------------------------------------------
using UnityEngine;
using UnityEditor;
using System.Collections;
//添加組件菜單
[AddComponentMenu("淺墨's Toolkit v1.0/ShowObjectInfo")]
//開(kāi)始ShowObjectInfo類(lèi)
public class ShowObjectInfo : MonoBehaviour
{
//------------------------------------------【變量聲明部分】----------------------------------------------------
// 說(shuō)明:變量聲明部分
//------------------------------------------------------------------------------------------------------------------
public string text="鍵入你自己的內(nèi)容 by淺墨";//文本內(nèi)容
public Camera TargetCamera;//面對(duì)的攝像機(jī)
public bool ShowInfoInGamePlay = true;//是否在游戲運(yùn)行時(shí)顯示此信息框的標(biāo)識(shí)符
public bool ShowInfoInSceneEditor = false;//是否在場(chǎng)景編輯時(shí)顯示此信息框的標(biāo)識(shí)符
private static GUIStyle style;//GUI風(fēng)格
//------------------------------------------【GUI 風(fēng)格的設(shè)置】--------------------------------------------------
// 說(shuō)明:設(shè)定GUI風(fēng)格
//------------------------------------------------------------------------------------------------------------------
private static GUIStyle Style
{
get
{
if (style == null)
{
//新建一個(gè)largeLabel的GUI風(fēng)格
style = new GUIStyle(EditorStyles.largeLabel);
//設(shè)置文本居中對(duì)齊
style.alignment = TextAnchor.MiddleCenter;
//設(shè)置GUI的文本顏色
style.normal.textColor = new Color(0.9f, 0.9f, 0.9f);
//設(shè)置GUI的文本字體大小
style.fontSize = 26;
}
return style;
}
}
//-----------------------------------------【OnGUI()函數(shù)】-----------------------------------------------------
// 說(shuō)明:游戲運(yùn)行時(shí)GUI的顯示
//------------------------------------------------------------------------------------------------------------------
void OnGUI( )
{
//ShowInfoInGamePlay為真時(shí),才進(jìn)行繪制
if (ShowInfoInGamePlay)
{
//---------------------------------【1.光線(xiàn)投射判斷&計(jì)算位置坐標(biāo)】-------------------------------
//定義一條射線(xiàn)
Ray ray = new Ray(transform.position + TargetCamera.transform.up * 6f, -TargetCamera.transform.up);
//定義光線(xiàn)投射碰撞
RaycastHit raycastHit;
//進(jìn)行光線(xiàn)投射操作,第一個(gè)參數(shù)為光線(xiàn)的開(kāi)始點(diǎn)和方向怒炸,第二個(gè)參數(shù)為光線(xiàn)碰撞器碰到哪里的輸出信息带饱,第三個(gè)參數(shù)為光線(xiàn)的長(zhǎng)度
collider.Raycast(ray, out raycastHit, Mathf.Infinity);
//計(jì)算距離,為當(dāng)前攝像機(jī)位置減去碰撞位置的長(zhǎng)度
float distance = (TargetCamera.transform.position - raycastHit.point).magnitude;
//設(shè)置字體大小阅羹,在26到12之間插值
float fontSize = Mathf.Lerp(26, 12, distance / 10f);
//將得到的字體大小賦給Style.fontSize
Style.fontSize = (int)fontSize;
//將文字位置取為得到的光線(xiàn)碰撞位置上方一點(diǎn)
Vector3 worldPositon = raycastHit.point + TargetCamera.transform.up * distance * 0.03f;
//世界坐標(biāo)轉(zhuǎn)屏幕坐標(biāo)
Vector3 screenPosition = TargetCamera.WorldToScreenPoint(worldPositon);
//z坐標(biāo)值的判斷勺疼,z值小于零就返回
if (screenPosition.z <= 0){return;}
//翻轉(zhuǎn)Y坐標(biāo)值
screenPosition.y = Screen.height - screenPosition.y;
//獲取文本尺寸
Vector2 stringSize = Style.CalcSize(new GUIContent(text));
//計(jì)算文本框坐標(biāo)
Rect rect = new Rect(0f, 0f, stringSize.x + 6, stringSize.y + 4);
//設(shè)定文本框中心坐標(biāo)
rect.center = screenPosition - Vector3.up * rect.height * 0.5f;
//----------------------------------【2.GUI繪制】---------------------------------------------
//開(kāi)始繪制一個(gè)簡(jiǎn)單的文本框
Handles.BeginGUI();
//繪制灰底背景
GUI.color = new Color(0f, 0f, 0f, 0.8f);
GUI.DrawTexture(rect, EditorGUIUtility.whiteTexture);
//繪制文字
GUI.color = new Color(1, 1, 1, 0.8f);
GUI.Label(rect, text, Style);
//結(jié)束繪制
Handles.EndGUI();
}
}
//-------------------------------------【OnDrawGizmos()函數(shù)】---------------------------------------------
// 說(shuō)明:場(chǎng)景編輯器中GUI的顯示
//------------------------------------------------------------------------------------------------------------------
void OnDrawGizmos()
{
//ShowInfoInSeneEditor為真時(shí),才進(jìn)行繪制
if (ShowInfoInSceneEditor)
{
//----------------------------------------【1.光線(xiàn)投射判斷&計(jì)算位置坐標(biāo)】----------------------------------
//定義一條射線(xiàn)
Ray ray = new Ray(transform.position + Camera.current.transform.up * 6f, -Camera.current.transform.up);
//定義光線(xiàn)投射碰撞
RaycastHit raycastHit;
//進(jìn)行光線(xiàn)投射操作,第一個(gè)參數(shù)為光線(xiàn)的開(kāi)始點(diǎn)和方向捏鱼,第二個(gè)參數(shù)為光線(xiàn)碰撞器碰到哪里的輸出信息恢口,第三個(gè)參數(shù)為光線(xiàn)的長(zhǎng)度
collider.Raycast(ray, out raycastHit, Mathf.Infinity);
//計(jì)算距離,為當(dāng)前攝像機(jī)位置減去碰撞位置的長(zhǎng)度
float distance = (Camera.current.transform.position - raycastHit.point).magnitude;
//設(shè)置字體大小穷躁,在26到12之間插值
float fontSize = Mathf.Lerp(26, 12, distance / 10f);
//將得到的字體大小賦給Style.fontSize
Style.fontSize = (int)fontSize;
//將文字位置取為得到的光線(xiàn)碰撞位置上方一點(diǎn)
Vector3 worldPositon = raycastHit.point + Camera.current.transform.up * distance * 0.03f;
//世界坐標(biāo)轉(zhuǎn)屏幕坐標(biāo)
Vector3 screenPosition = Camera.current.WorldToScreenPoint(worldPositon);
//z坐標(biāo)值的判斷,z值小于零就返回
if (screenPosition.z <= 0) { return; }
//翻轉(zhuǎn)Y坐標(biāo)值
screenPosition.y = Screen.height - screenPosition.y;
//獲取文本尺寸
Vector2 stringSize = Style.CalcSize(new GUIContent(text));
//計(jì)算文本框坐標(biāo)
Rect rect = new Rect(0f, 0f, stringSize.x + 6, stringSize.y + 4);
//設(shè)定文本框中心坐標(biāo)
rect.center = screenPosition - Vector3.up * rect.height * 0.5f;
//----------------------------------【2.GUI繪制】---------------------------------------------
//開(kāi)始繪制一個(gè)簡(jiǎn)單的文本框
Handles.BeginGUI();
//繪制灰底背景
GUI.color = new Color(0f, 0f, 0f, 0.8f);
GUI.DrawTexture(rect, EditorGUIUtility.whiteTexture);
//繪制文字
GUI.color = new Color(1, 1, 1, 0.8f);
GUI.Label(rect, text, Style);
//結(jié)束繪制
Handles.EndGUI();
}
}
}
//預(yù)編譯命令結(jié)束
#endif
這個(gè)腳本的用法倒是很簡(jiǎn)單,在代碼的說(shuō)明部分已經(jīng)詳細(xì)寫(xiě)出问潭,在這里我們?cè)倭谐鲆槐椋?/p>
第一步:在Unity中拖拽此腳本到某物體之上猿诸,或在Inspector中[Add Component]->[淺墨's Toolkit v1.0]->[ShowObjectInfo]
第二步:在Inspector里,ShowObject Info 欄中的TargetCamera參數(shù)中選擇需面向的攝像機(jī),如Main Camera,F(xiàn)irstPerson Controller等
第三步:在text參數(shù)里填需要顯示輸出的文字狡忙。
第四步:完成梳虽。運(yùn)行游戲或在場(chǎng)景編輯器Scene中查看顯示效果。
也就是拖拽ShowObjectInfo腳本或者直接添加組件給需要附加文字的物體灾茁,然后在Inspector中輸入需要顯示的文字窜觉,然后選擇其面對(duì)的攝像機(jī)就可以了。
我們將ShowObjectInfo腳本拖拽給上文中剛剛變得炫酷外形黑科技的Capsule:
那么他在Inspector就多了一個(gè)“ShowObject Info(Script)”組件北专,將該組件的Text項(xiàng)中填上“凹凸紋理+邊緣發(fā)光效果”禀挫,TargetCamera中填上First Person Controller的子物體Main Camera:
最后,得到的效果就是這樣:
五拓颓、總結(jié)语婴、配套資源&最終工程下載
好了,本篇的文章到這里就大概結(jié)束了驶睦。
今天講的內(nèi)容還是非常多的砰左,對(duì)于新接觸Unity的朋友們來(lái)說(shuō)或許還得好好消化消化,而熟悉Unity的朋友應(yīng)該很快就可以看懂场航,或者覺(jué)得淺墨講了一堆廢話(huà)缠导,orz。
這篇文章的內(nèi)容說(shuō)白了就非常簡(jiǎn)單溉痢,也就是新建工程僻造,然后導(dǎo)入三個(gè)淺墨提前準(zhǔn)備好的unitypackage游戲資源,點(diǎn)一點(diǎn)鼠標(biāo)拖動(dòng)拖動(dòng)腳本适室,新建一個(gè)Shader嫡意,寫(xiě)點(diǎn)代碼,再創(chuàng)建一個(gè)Material,Shader賦給這個(gè)Material捣辆,最后創(chuàng)建一個(gè)膠囊狀Capsule蔬螟,Material賦給這個(gè)Capsule,點(diǎn)運(yùn)行查看最終效果汽畴。一切旧巾,就是這么簡(jiǎn)單。:)
本文配套的三個(gè)unitypackage打包請(qǐng)點(diǎn)擊此處下載:
【淺墨Unity3D Shader編程】之一 配套的三個(gè)unitypackage打包下載
本文最終的Unity工程請(qǐng)點(diǎn)擊此處下載:
【淺墨Unity3D Shader編程】之一 配套Unity工程
最后放幾張最終的場(chǎng)景美圖吧忍些。
站在亭子上看世界:
逼真的光暈:
漂亮的天空:
亂真的水面:
藍(lán)天和草地樹(shù)木交相輝映:
OK鲁猩,全文到此結(jié)束。
新的游戲編程之旅已經(jīng)開(kāi)啟罢坝,下周一廓握,我們不見(jiàn)不散。