国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

貓都能學(xué)會(huì)的Unity3D Shader入門指南(一)

2019-11-09 19:04:35
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

Unity Shader教程

動(dòng)機(jī)

自己使用Unity3D也有一段時(shí)間了,但是很多時(shí)候是流于表面,更多地是把這個(gè)引擎簡(jiǎn)單地用作腳本控制,而對(duì)更深入一些的層次幾乎沒(méi)有了解。雖然說(shuō)Unity引擎設(shè)計(jì)的初衷就是創(chuàng)建簡(jiǎn)單的不需要開發(fā)者操心的誰(shuí)都能用的3D引擎,但是只是膚淺的使用,可能是無(wú)法達(dá)到隨心所欲的境地的,因此,這種狀況必須改變!從哪里開始呢,貌似有句話叫做會(huì)寫Shader的都是高手,于是,想大概看看從Shader開始能不能使自己到達(dá)的層次能再深入一些吧,再于是,有了這個(gè)系列(希望我能堅(jiān)持寫完它,雖然應(yīng)該會(huì)拖個(gè)半年左右)。

Unity3D的所有渲染工作都離不開著色器(Shader),如果你和我一樣最近開始對(duì)Shader編程比較感興趣的話,可能你和我有著同樣的困惑:如何開始?Unity3D提供了一些Shader的手冊(cè)和文檔(比如這里,這里和這里),但是一來(lái)內(nèi)容比較分散,二來(lái)學(xué)習(xí)階梯稍微陡峭了些。這對(duì)于像我這樣之前完全沒(méi)有接觸過(guò)有關(guān)內(nèi)容的新人來(lái)說(shuō)是相當(dāng)不友好的。國(guó)內(nèi)外雖然也有一些Shader的介紹和心得,但是也同樣存在內(nèi)容分散的問(wèn)題,很多教程前一章就只介紹了基本概念,接下來(lái)馬上就搬出一個(gè)超復(fù)雜的例子,對(duì)于很多基本的用法并沒(méi)有解釋。也許對(duì)于Shader熟練使用的開發(fā)者來(lái)說(shuō)是沒(méi)有問(wèn)題,但是我相信像我這樣的入門者也并不在少數(shù)。在多方尋覓無(wú)果后,我覺(jué)得有必要寫一份教程,來(lái)以一個(gè)入門者的角度介紹一些Shader開發(fā)的基本步驟。其實(shí)與其說(shuō)是教程,倒不如說(shuō)是一份自我總結(jié),希望能夠幫到有需要的人。

所以,本“教程”的對(duì)象是

總的來(lái)說(shuō)是新接觸Shader開發(fā)的人:也許你知道什么是Shader,也會(huì)使用別人的Shader,但是僅限于知道一些基本的內(nèi)建Shader名字,從來(lái)沒(méi)有打開它們查看其源碼。想要更多了解Shader和有需求要進(jìn)行Shader開發(fā)的開發(fā)者,但是之前并沒(méi)有Shader開發(fā)的經(jīng)驗(yàn)。

當(dāng)然,因?yàn)槲冶旧碓赟hader開發(fā)方面也是一個(gè)不折不扣的大菜鳥,本文很多內(nèi)容也只是在自己的理解加上一些可能不太靠譜的求證和總結(jié)。本文中的示例應(yīng)該會(huì)有更好的方式來(lái)實(shí)現(xiàn),因此您是高手并且恰巧路過(guò)的話,如果有好的方式來(lái)實(shí)現(xiàn)某些內(nèi)容,懇請(qǐng)您不吝留下評(píng)論,我會(huì)對(duì)本文進(jìn)行不斷更新和維護(hù)。

一些基本概念

Shader和Material

如果是進(jìn)行3D游戲開發(fā)的話,想必您對(duì)著兩個(gè)詞不會(huì)陌生。Shader(著色器)實(shí)際上就是一小段程序,它負(fù)責(zé)將輸入的Mesh(網(wǎng)格)以指定的方式和輸入的貼圖或者顏色等組合作用,然后輸出。繪圖單元可以依據(jù)這個(gè)輸出來(lái)將圖像繪制到屏幕上。輸入的貼圖或者顏色等,加上對(duì)應(yīng)的Shader,以及對(duì)Shader的特定的參數(shù)設(shè)置,將這些內(nèi)容(Shader及輸入?yún)?shù))打包存儲(chǔ)在一起,得到的就是一個(gè)Material(材質(zhì))。之后,我們便可以將材質(zhì)賦予合適的renderer(渲染器)來(lái)進(jìn)行渲染(輸出)了。

所以說(shuō)Shader并沒(méi)有什么特別神奇的,它只是一段規(guī)定好輸入(顏色,貼圖等)和輸出(渲染器能夠讀懂的點(diǎn)和顏色的對(duì)應(yīng)關(guān)系)的程序。而Shader開發(fā)者要做的就是根據(jù)輸入,進(jìn)行計(jì)算變換,產(chǎn)生輸出而已。

Shader大體上可以分為兩類,簡(jiǎn)單來(lái)說(shuō)

表面著色器(Surface Shader) - 為你做了大部分的工作,只需要簡(jiǎn)單的技巧即可實(shí)現(xiàn)很多不錯(cuò)的效果。類比卡片機(jī),上手以后不太需要很多努力就能拍出不錯(cuò)的效果。片段著色器(Fragment Shader) - 可以做的事情更多,但是也比較難寫。使用片段著色器的主要目的是可以在比較低的層級(jí)上進(jìn)行更復(fù)雜(或者針對(duì)目標(biāo)設(shè)備更高效)的開發(fā)。

因?yàn)槭侨腴T文章,所以之后的介紹將主要集中在表面著色器上。

Shader程序的基本結(jié)構(gòu)

因?yàn)橹鞔a可以說(shuō)專用性非常強(qiáng),因此人為地規(guī)定了它的基本結(jié)構(gòu)。一個(gè)普通的著色器的結(jié)構(gòu)應(yīng)該是這樣的:一段Shader程序的結(jié)構(gòu)

首先是一些屬性定義,用來(lái)指定這段代碼將有哪些輸入。接下來(lái)是一個(gè)或者多個(gè)的子著色器,在實(shí)際運(yùn)行中,哪一個(gè)子著色器被使用是由運(yùn)行的平臺(tái)所決定的。子著色器是代碼的主體,每一個(gè)子著色器中包含一個(gè)或者多個(gè)的Pass。在計(jì)算著色時(shí),平臺(tái)先選擇最優(yōu)先可以使用的著色器,然后依次運(yùn)行其中的Pass,然后得到輸出的結(jié)果。最后指定一個(gè)回滾,用來(lái)處理所有Subshader都不能運(yùn)行的情況(比如目標(biāo)設(shè)備實(shí)在太老,所有Subshader中都有其不支持的特性)。

需要提前說(shuō)明的是,在實(shí)際進(jìn)行表面著色器的開發(fā)時(shí),我們將直接在Subshader這個(gè)層次上寫代碼,系統(tǒng)將把我們的代碼編譯成若干個(gè)合適的Pass。廢話到此為止,下面讓我們真正實(shí)際進(jìn)入Shader的世界吧。

Hello Shader

百行文檔不如一個(gè)實(shí)例,下面給出一段簡(jiǎn)單的Shader代碼,然后根據(jù)代碼來(lái)驗(yàn)證下上面說(shuō)到的結(jié)構(gòu)和闡述一些基本的Shader語(yǔ)法。因?yàn)楸疚氖轻槍?duì)Unity3D來(lái)寫Shader的,所以也使用Unity3D來(lái)演示吧。首先,新建一個(gè)Shader,可以在PRoject面板中找到,Create,選擇Shader,然后將其命名為Diffuse Texture

在Unity3D中新建一個(gè)Shader

隨便用個(gè)文本編輯器打開剛才新建的Shader:

Shader "Custom/Diffuse Texture" {    Properties {        _MainTex ("Base (RGB)", 2D) = "white" {}    }    SubShader {        Tags { "RenderType"="Opaque" }        LOD 200        CGPROGRAM        #pragma surface surf Lambert        sampler2D _MainTex;        struct Input {            float2 uv_MainTex;        };        void surf (Input IN, inout SurfaceOutput o) {            half4 c = tex2D (_MainTex, IN.uv_MainTex);            o.Albedo = c.rgb;            o.Alpha = c.a;        }        ENDCG    }     FallBack "Diffuse"}

如果您之前沒(méi)怎么看過(guò)Shader代碼的話,估計(jì)細(xì)節(jié)上會(huì)看不太懂。但是有了上面基本結(jié)構(gòu)的介紹,您應(yīng)該可以識(shí)別出這個(gè)Shader的構(gòu)成,比如一個(gè)Properties部分,一個(gè)SubShader,以及一個(gè)FallBack。另外,第一行只是這個(gè)Shader的聲明并為其指定了一個(gè)名字,比如我們的實(shí)例Shader,你可以在材質(zhì)面板選擇Shader時(shí)在對(duì)應(yīng)的位置找到這個(gè)Shader。

在Unity3D中找到剛才新建的Shader

接下來(lái)我們講逐句講解這個(gè)Shader,以期明了每一個(gè)語(yǔ)句的意義。

屬性

Properties{}中定義著色器屬性,在這里定義的屬性將被作為輸入提供給所有的子著色器。每一條屬性的定義的語(yǔ)法是這樣的:

_Name("Display Name", type) = defaultValue[{options}]

_Name - 屬性的名字,簡(jiǎn)單說(shuō)就是變量名,在之后整個(gè)Shader代碼中將使用這個(gè)名字來(lái)獲取該屬性的內(nèi)容Display Name - 這個(gè)字符串將顯示在Unity的材質(zhì)編輯器中作為Shader的使用者可讀的內(nèi)容type - 這個(gè)屬性的類型,可能的type所表示的內(nèi)容有以下幾種:Color - 一種顏色,由RGBA(紅綠藍(lán)和透明度)四個(gè)量來(lái)定義;2D - 一張2的階數(shù)大小(256,512之類)的貼圖。這張貼圖將在采樣后被轉(zhuǎn)為對(duì)應(yīng)基于模型UV的每個(gè)像素的顏色,最終被顯示出來(lái);Rect - 一個(gè)非2階數(shù)大小的貼圖;Cube - 即Cube map texture(立方體紋理),簡(jiǎn)單說(shuō)就是6張有聯(lián)系的2D貼圖的組合,主要用來(lái)做反射效果(比如天空盒和動(dòng)態(tài)反射),也會(huì)被轉(zhuǎn)換為對(duì)應(yīng)點(diǎn)的采樣;Range(min, max) - 一個(gè)介于最小值和最大值之間的浮點(diǎn)數(shù),一般用來(lái)當(dāng)作調(diào)整Shader某些特性的參數(shù)(比如透明度渲染的截止值可以是從0至1的值等);Float - 任意一個(gè)浮點(diǎn)數(shù);Vector - 一個(gè)四維數(shù);defaultValue 定義了這個(gè)屬性的默認(rèn)值,通過(guò)輸入一個(gè)符合格式的默認(rèn)值來(lái)指定對(duì)應(yīng)屬性的初始值(某些效果可能需要某些特定的參數(shù)值來(lái)達(dá)到需要的效果,雖然這些值可以在之后在進(jìn)行調(diào)整,但是如果默認(rèn)就指定為想要的值的話就省去了一個(gè)個(gè)調(diào)整的時(shí)間,方便很多)。Color - 以0~1定義的rgba顏色,比如(1,1,1,1);2D/Rect/Cube - 對(duì)于貼圖來(lái)說(shuō),默認(rèn)值可以為一個(gè)代表默認(rèn)tint顏色的字符串,可以是空字符串或者"white","black","gray","bump"中的一個(gè)Float,Range - 某個(gè)指定的浮點(diǎn)數(shù)Vector - 一個(gè)4維數(shù),寫為 (x,y,z,w)另外還有一個(gè){option},它只對(duì)2D,Rect或者Cube貼圖有關(guān),在寫輸入時(shí)我們最少要在貼圖之后寫一對(duì)什么都不含的空白的{},當(dāng)我們需要打開特定選項(xiàng)時(shí)可以把其寫在這對(duì)花括號(hào)內(nèi)。如果需要同時(shí)打開多個(gè)選項(xiàng),可以使用空白分隔。可能的選擇有ObjectLinear, EyeLinear, SphereMap, CubeReflect, CubeNormal中的一個(gè),這些都是OpenGL中TexGen的模式,具體的留到后面有機(jī)會(huì)再說(shuō)。

所以,一組屬性的申明看起來(lái)也許會(huì)是這個(gè)樣子的

//Define a color with a default value of semi-transparent blue_MainColor ("Main Color", Color) = (0,0,1,0.5)//Define a texture with a default of white_Texture ("Texture", 2D) = "white" {}

現(xiàn)在看懂上面那段Shader(以及其他所有Shader)的Properties部分應(yīng)該不會(huì)有任何問(wèn)題了。接下來(lái)就是SubShader部分了。

Tags

表面著色器可以被若干的標(biāo)簽(tags)所修飾,而硬件將通過(guò)判定這些標(biāo)簽來(lái)決定什么時(shí)候調(diào)用該著色器。比如我們的例子中SubShader的第一句

Tags { "RenderType"="Opaque" }

告訴了系統(tǒng)應(yīng)該在渲染非透明物體時(shí)調(diào)用我們。Unity定義了一些列這樣的渲染過(guò)程,與RenderType是Opaque相對(duì)應(yīng)的顯而易見(jiàn)的是"RenderType" = "Transparent",表示渲染含有透明效果的物體時(shí)調(diào)用。在這里Tags其實(shí)暗示了你的Shader輸出的是什么,如果輸出中都是非透明物體,那寫在Opaque里;如果想渲染透明或者半透明的像素,那應(yīng)該寫在Transparent中。

另外比較有用的標(biāo)簽還有"IgnoreProjector"="True"(不被Projectors影響),"ForceNoShadowCasting"="True"(從不產(chǎn)生陰影)以及"Queue"="xxx"(指定渲染順序隊(duì)列)。這里想要著重說(shuō)一下的是Queue這個(gè)標(biāo)簽,如果你使用Unity做過(guò)一些透明和不透明物體的混合的話,很可能已經(jīng)遇到過(guò)不透明物體無(wú)法呈現(xiàn)在透明物體之后的情況。這種情況很可能是由于Shader的渲染順序不正確導(dǎo)致的。Queue指定了物體的渲染順序,預(yù)定義的Queue有:

Background - 最早被調(diào)用的渲染,用來(lái)渲染天空盒或者背景Geometry - 這是默認(rèn)值,用來(lái)渲染非透明物體(普通情況下,場(chǎng)景中的絕大多數(shù)物體應(yīng)該是非透明的)AlphaTest - 用來(lái)渲染經(jīng)過(guò)Alpha Test的像素,單獨(dú)為AlphaTest設(shè)定一個(gè)Queue是出于對(duì)效率的考慮Transparent - 以從后往前的順序渲染透明物體Overlay - 用來(lái)渲染疊加的效果,是渲染的最后階段(比如鏡頭光暈等特效)

這些預(yù)定義的值本質(zhì)上是一組定義整數(shù),Background = 1000, Geometry = 2000, AlphaTest = 2450, Transparent = 3000,最后Overlay = 4000。在我們實(shí)際設(shè)置Queue值時(shí),不僅能使用上面的幾個(gè)預(yù)定義值,我們也可以指定自己的Queue值,寫成類似這樣:"Queue"="Transparent+100",表示一個(gè)在Transparent之后100的Queue上進(jìn)行調(diào)用。通過(guò)調(diào)整Queue值,我們可以確保某些物體一定在另一些物體之前或者之后渲染,這個(gè)技巧有時(shí)候很有用處。

LOD

LOD很簡(jiǎn)單,它是Level of Detail的縮寫,在這里例子里我們指定了其為200(其實(shí)這是Unity的內(nèi)建Diffuse著色器的設(shè)定值)。這個(gè)數(shù)值決定了我們能用什么樣的Shader。在Unity的Quality Settings中我們可以設(shè)定允許的最大LOD,當(dāng)設(shè)定的LOD小于SubShader所指定的LOD時(shí),這個(gè)SubShader將不可用。Unity內(nèi)建Shader定義了一組LOD的數(shù)值,我們?cè)趯?shí)現(xiàn)自己的Shader的時(shí)候可以將其作為參考來(lái)設(shè)定自己的LOD數(shù)值,這樣在之后調(diào)整根據(jù)設(shè)備圖形性能來(lái)調(diào)整畫質(zhì)時(shí)可以進(jìn)行比較精確的控制。

VertexLit及其系列 = 100Decal, Reflective VertexLit = 150Diffuse = 200Diffuse Detail, Reflective Bumped Unlit, Reflective Bumped VertexLit = 250Bumped, Specular = 300Bumped Specular = 400Parallax = 500Parallax Specular = 600

Shader本體

前面雜項(xiàng)說(shuō)完了,終于可以開始看看最主要的部分了,也就是將輸入轉(zhuǎn)變?yōu)檩敵龅拇a部分。為了方便看,請(qǐng)容許我把上面的SubShader的主題部分抄寫一遍

CGPROGRAM#pragma surface surf Lambertsampler2D _MainTex;struct Input {    float2 uv_MainTex;};void surf (Input IN, inout SurfaceOutput o) {    half4 c = tex2D (_MainTex, IN.uv_MainTex);    o.Albedo = c.rgb;    o.Alpha = c.a;}ENDCG

還是逐行來(lái)看,首先是CGPROGRAM。這是一個(gè)開始標(biāo)記,表明從這里開始是一段CG程序(我們?cè)趯慤nity的Shader時(shí)用的是Cg/HLSL語(yǔ)言)。最后一行的ENDCG與CGPROGRAM是對(duì)應(yīng)的,表明CG程序到此結(jié)束。

接下來(lái)是是一個(gè)編譯指令:#pragma surface surf Lambert,它聲明了我們要寫一個(gè)表面Shader,并指定了光照模型。它的寫法是這樣的

#pragma surface surfaceFunction lightModel [optionalparams]

surface - 聲明的是一個(gè)表面著色器surfaceFunction - 著色器代碼的方法的名字lightModel - 使用的光照模型。

所以在我們的例子中,我們聲明了一個(gè)表面著色器,實(shí)際的代碼在surf函數(shù)中(在下面能找到該函數(shù)),使用Lambert(也就是普通的diffuse)作為光照模型。

接下來(lái)一句sampler2D _MainTex;,sampler2D是個(gè)啥?其實(shí)在CG中,sampler2D就是和texture所綁定的一個(gè)數(shù)據(jù)容器接口。等等..這個(gè)說(shuō)法還是太復(fù)雜了,簡(jiǎn)單理解的話,所謂加載以后的texture(貼圖)說(shuō)白了不過(guò)是一塊內(nèi)存存儲(chǔ)的,使用了RGB(也許還有A)通道,且每個(gè)通道8bits的數(shù)據(jù)。而具體地想知道像素與坐標(biāo)的對(duì)應(yīng)關(guān)系,以及獲取這些數(shù)據(jù),我們總不能一次一次去自己計(jì)算內(nèi)存地址或者偏移,因此可以通過(guò)sampler2D來(lái)對(duì)貼圖進(jìn)行操作。更簡(jiǎn)單地理解,sampler2D就是GLSL中的2D貼圖的類型,相應(yīng)的,還有sampler1D,sampler3D,samplerCube等等格式。

解釋通了sampler2D是什么之后,還需要解釋下為什么在這里需要一句對(duì)_MainTex的聲明,之前我們不是已經(jīng)在Properties里聲明過(guò)它是貼圖了么。答案是我們用來(lái)實(shí)例的這個(gè)shader其實(shí)是由兩個(gè)相對(duì)獨(dú)立的塊組成的,外層的屬性聲明,回滾等等是Unity可以直接使用和編譯的ShaderLab;而現(xiàn)在我們是在CGPROGRAM...ENDCG這樣一個(gè)代碼塊中,這是一段CG程序。對(duì)于這段CG程序,要想訪問(wèn)在Properties中所定義的變量的話,必須使用和之前變量相同的名字進(jìn)行聲明。于是其實(shí)sampler2D _MainTex;做的事情就是再次聲明并鏈接了_MainTex,使得接下來(lái)的CG程序能夠使用這個(gè)變量。

終于可以繼續(xù)了。接下來(lái)是一個(gè)struct結(jié)構(gòu)體。相信大家對(duì)于結(jié)構(gòu)體已經(jīng)很熟悉了,我們先跳過(guò)之,直接看下面的的surf函數(shù)。上面的#pragma段已經(jīng)指出了我們的著色器代碼的方法的名字叫做surf,那沒(méi)跑兒了,就是這段代碼是我們的著色器的工作核心。我們已經(jīng)說(shuō)過(guò)不止一次,著色器就是給定了輸入,然后給出輸出進(jìn)行著色的代碼。CG規(guī)定了聲明為表面著色器的方法(就是我們這里的surf)的參數(shù)類型和名字,因此我們沒(méi)有權(quán)利決定surf的輸入輸出參數(shù)的類型,只能按照規(guī)定寫。這個(gè)規(guī)定就是第一個(gè)參數(shù)是一個(gè)Input結(jié)構(gòu),第二個(gè)參數(shù)是一個(gè)inout的SurfaceOutput結(jié)構(gòu)。

它們分別是什么呢?Input其實(shí)是需要我們?nèi)ザx的結(jié)構(gòu),這給我們提供了一個(gè)機(jī)會(huì),可以把所需要參與計(jì)算的數(shù)據(jù)都放到這個(gè)Input結(jié)構(gòu)中,傳入surf函數(shù)使用;SurfaceOutput是已經(jīng)定義好了里面類型輸出結(jié)構(gòu),但是一開始的時(shí)候內(nèi)容暫時(shí)是空白的,我們需要向里面填寫輸出,這樣就可以完成著色了。先仔細(xì)看看INPUT吧,現(xiàn)在可以跳回來(lái)看上面定義的INPUT結(jié)構(gòu)體了:

struct Input {    float2 uv_MainTex;};

作為輸入的結(jié)構(gòu)體必須命名為Input,這個(gè)結(jié)構(gòu)體中定義了一個(gè)float2的變量…你沒(méi)看錯(cuò)我也沒(méi)打錯(cuò),就是float2,表示浮點(diǎn)數(shù)的float后面緊跟一個(gè)數(shù)字2,這又是什么意思呢?其實(shí)沒(méi)什么魔法,float和vec都可以在之后加入一個(gè)2到4的數(shù)字,來(lái)表示被打包在一起的2到4個(gè)同類型數(shù)。比如下面的這些定義:

//Define a 2d vector variablevec2 coordinate;//Define a color variablefloat4 color;//Multiply out a colorfloat3 multipliedColor = color.rgb * coordinate.x;

在訪問(wèn)這些值時(shí),我們即可以只使用名稱來(lái)獲得整組值,也可以使用下標(biāo)的方式(比如.xyzw,.rgba或它們的部分比如.x等等)來(lái)獲得某個(gè)值。在這個(gè)例子里,我們聲明了一個(gè)叫做uv_MainTex的包含兩個(gè)浮點(diǎn)數(shù)的變量。

如果你對(duì)3D開發(fā)稍有耳聞的話,一定不會(huì)對(duì)uv這兩個(gè)字母感到陌生。UV mapping的作用是將一個(gè)2D貼圖上的點(diǎn)按照一定規(guī)則映射到3D模型上,是3D渲染中最常見(jiàn)的一種頂點(diǎn)處理手段。在CG程序中,我們有這樣的約定,在一個(gè)貼圖變量(在我們例子中是_MainTex)之前加上uv兩個(gè)字母,就代表提取它的uv值(其實(shí)就是兩個(gè)代表貼圖上點(diǎn)的二維坐標(biāo) )。我們之后就可以在surf程序中直接通過(guò)訪問(wèn)uv_MainTex來(lái)取得這張貼圖當(dāng)前需要計(jì)算的點(diǎn)的坐標(biāo)值了。

如果你堅(jiān)持看到這里了,那要恭喜你,因?yàn)殡x最后成功讀完一個(gè)Shader只有一步之遙。我們回到surf函數(shù),它的兩有參數(shù),第一個(gè)是Input,我們已經(jīng)明白了:在計(jì)算輸出時(shí)Shader會(huì)多次調(diào)用surf函數(shù),每次給入一個(gè)貼圖上的點(diǎn)坐標(biāo),來(lái)計(jì)算輸出。第二個(gè)參數(shù)是一個(gè)可寫的SurfaceOutput,SurfaceOutput是預(yù)定義的輸出結(jié)構(gòu),我們的surf函數(shù)的目標(biāo)就是根據(jù)輸入把這個(gè)輸出結(jié)構(gòu)填上。SurfaceOutput結(jié)構(gòu)體的定義如下

struct SurfaceOutput {    half3 Albedo;     //像素的顏色    half3 Normal;     //像素的法向值    half3 Emission;   //像素的發(fā)散顏色    half Specular;    //像素的鏡面高光    half Gloss;       //像素的發(fā)光強(qiáng)度    half Alpha;       //像素的透明度};

這里的half和我們常見(jiàn)float與double類似,都表示浮點(diǎn)數(shù),只不過(guò)精度不一樣。也許你很熟悉單精度浮點(diǎn)數(shù)(float或者single)和雙精度浮點(diǎn)數(shù)(double),這里的half指的是半精度浮點(diǎn)數(shù),精度最低,運(yùn)算性能相對(duì)比高精度浮點(diǎn)數(shù)高一些,因此被大量使用。

在例子中,我們做的事情非常簡(jiǎn)單:

half4 c = tex2D (_MainTex, IN.uv_MainTex);o.Albedo = c.rgb;o.Alpha = c.a;

這里用到了一個(gè)tex2d函數(shù),這是CG程序中用來(lái)在一張貼圖中對(duì)一個(gè)點(diǎn)進(jìn)行采樣的方法,返回一個(gè)float4。這里對(duì)_MainTex在輸入點(diǎn)上進(jìn)行了采樣,并將其顏色的rbg值賦予了輸出的像素顏色,將a值賦予透明度。于是,著色器就明白了應(yīng)當(dāng)怎樣工作:即找到貼圖上對(duì)應(yīng)的uv點(diǎn),直接使用顏色信息來(lái)進(jìn)行著色,over。

接下來(lái)...

我想現(xiàn)在你已經(jīng)能讀懂一些最簡(jiǎn)單的Shader了,接下來(lái)我推薦的是參考Unity的Surface Shader Examples多接觸一些各種各樣的基本Shader。在這篇教程的基礎(chǔ)上,配合一些google的工作,完全看懂這個(gè)shader示例頁(yè)面應(yīng)該不成問(wèn)題。如果能做到無(wú)壓力看懂,那說(shuō)明你已經(jīng)有良好的基礎(chǔ)可以前進(jìn)到Shader的更深的層次了(也許等不到我的下一篇教程就可以自己開始動(dòng)手寫些效果了);如果暫時(shí)還是有困難,那也沒(méi)有關(guān)系,Shader學(xué)習(xí)絕對(duì)是一個(gè)漸進(jìn)的過(guò)程,因?yàn)橛泻芏嗉s定和常用技巧,多積累和實(shí)踐自然會(huì)進(jìn)步并掌握。

在接下來(lái)的教程里,打算通過(guò)介紹一些實(shí)際例子以及從基礎(chǔ)開始實(shí)際逐步動(dòng)手實(shí)現(xiàn)一個(gè)復(fù)雜一點(diǎn)的例子,讓我們能看到shader在真正使用中的威力。我希望能盡快寫完這個(gè)系列,但是無(wú)奈時(shí)間確實(shí)有限,所以我也不知道什么時(shí)候能出爐...寫好的時(shí)候我會(huì)更改這段內(nèi)容并指向新的文章。您要是擔(dān)心錯(cuò)過(guò)的話,也可以使用郵件訂閱或者訂閱本站的rss(雖然Google Reader已經(jīng)關(guān)了- -)。


發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 建昌县| 武安市| 甘泉县| 高密市| 桂林市| 聂拉木县| 蓝山县| 克什克腾旗| 盐津县| 闽清县| 禄丰县| 故城县| 上栗县| 安仁县| 丰县| 铁岭县| 商河县| 全南县| 罗山县| 个旧市| 灵川县| 金山区| 莎车县| 墨脱县| 油尖旺区| 陕西省| 嘉兴市| 盐亭县| 郸城县| 定兴县| 定西市| 内黄县| 西藏| 桑植县| 蒲江县| 新竹市| 合江县| 宝山区| 庐江县| 丹棱县| 漳浦县|