日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

【译】Unity3D Shader 新手教程(1/6)

發(fā)布時(shí)間:2024/1/8 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【译】Unity3D Shader 新手教程(1/6) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

剛開始接觸Unity3D Shader編程時(shí),你會(huì)發(fā)現(xiàn)有關(guān)shader的文檔相當(dāng)散,這也造成初學(xué)者對(duì)Unity3D Shader編程望而卻步。該系列教程的第一篇文章(譯者注:即本文,后續(xù)還有5篇文章)詳細(xì)介紹了Unity3D中的表面著色器(Surface Shader)的,為學(xué)習(xí)更復(fù)雜的Shader編程打下基礎(chǔ)。

動(dòng)機(jī)

如果你是剛剛接觸Shader編程的新手,你可能不知道從何開始踏出Shader編程的第一步。本教程將帶你一步步完成一個(gè)表面著色器(Surface Shader)和片段著色器(Fragment Shader)。本教程也將介紹在Unity3D Shader編程中所使用的一些函數(shù)和變量,這些內(nèi)容可能和你在網(wǎng)上看到的不一樣哦!

如果你滿足下面的條件,我覺(jué)得你應(yīng)該看看這篇文章:

  • 如果你是shader編程的新手。
  • 你想在你的游戲中使用shader做一些很炫酷的效果,但是你在網(wǎng)上找不到可用的Shader(譯者注:o(╯□╰)o自己動(dòng)手豐衣足食)
  • 由于缺乏對(duì)基礎(chǔ)知識(shí)的了解,造成不能隨心所欲使用Strumpy著色器編輯器譯者注:Strumpy Shader Editor,一種圖形化編寫shader的方式,看著很誘人!)
  • 你想在你的shader代碼中手動(dòng)處理紋理(Textures)

本文是該系列教程的第一篇文章,隨后我們會(huì)制作一些更復(fù)雜的shader。相比起來(lái),第一篇文章確實(shí)很簡(jiǎn)單。

關(guān)于作者

我也是Shader編程的新手----所以我決定寫這篇教程幫助大家入門——我當(dāng)初也在入門上遇到很多苦惱。事實(shí)上我并不是一個(gè)Shader編程專家。

當(dāng)我想了解Shader編程時(shí),我曾反復(fù)閱讀官方文檔,但是我最終發(fā)現(xiàn)官方文檔講述的順序并不適合我學(xué)習(xí)shader。所以我覺(jué)得我應(yīng)該寫一篇教程,并分享我所學(xué)到的知識(shí)。不過(guò)寫完教程之后,我發(fā)現(xiàn)再次閱讀官方文檔時(shí),覺(jué)得明白多了。

盡管本教程中的所有例子都能正常運(yùn)行,但是我相信肯定有更好shaders實(shí)現(xiàn)這些例子。如果聰明的你對(duì)這些例子中的shaders有更好的建議,請(qǐng)?jiān)谠u(píng)論區(qū)留言!

我之所以學(xué)習(xí)shader編程是因?yàn)槲倚枰谖覄?chuàng)建的游戲世界中創(chuàng)建些東西,但這個(gè)游戲世界創(chuàng)建起來(lái)有個(gè)麻煩之處,因?yàn)樗怯刹煌巧M成的,而我必須創(chuàng)建由多個(gè)部分組成的一個(gè)統(tǒng)一網(wǎng)格(mesh)。所以我只能對(duì)每個(gè)角色使用一次繪制調(diào)用(draw call)。(譯者注:完全不知道他在講什么?所以我把原文放在下面給大家評(píng)評(píng)理)

My reason for getting into shader programming was to build something that I needed for a world populated with an endless array of different characters.? I needed to build a combined mesh out of multiple parts so I only have one draw call per character.

通過(guò)打開和關(guān)閉角色的穿衣效果,我使用Megafiers(一個(gè)變形插件)修改了角色的基本網(wǎng)格(base meshes)。其中的困難在于我只有一個(gè)紋理(texture),但是我卻想給每個(gè)角色的皮膚,服飾以及其他的特征使用不同的顏色。我想到一個(gè)方法----對(duì)每個(gè)角色使用不同的3個(gè)4x4紋理,并使用一個(gè)shader來(lái)給模型上色。我將在整個(gè)教程中詳細(xì)描述我做的這個(gè)shader,但是現(xiàn)在—我想你們已經(jīng)迫不及待地想看我創(chuàng)建的角色表演一段即興的快閃舞(flash mob dance)(譯者注:網(wǎng)上截的圖片)

著色器和材質(zhì)(shaders&materials)

一個(gè)shader所做的就是將一個(gè)模型的網(wǎng)格(mesh)渲染到屏幕上。Shader可以被定義為一系列的屬性(譯者注:就像一個(gè)函數(shù)里面的參數(shù)一樣,你可以改變函數(shù)的不同賦值來(lái)改變函數(shù)的輸出結(jié)果),你可以通過(guò)改變這些屬性來(lái)改變模型渲染到屏幕上的效果。而這些屬性被存放起來(lái),放到一個(gè)叫做材質(zhì)(material)的地方。

Unity3D Shader有以下幾種

  • 表面著色器(surface shader)----后臺(tái)自動(dòng)為你做的絕大部分的工作,減少了你工作量,并且適合絕大多數(shù)需要shader的情況。
  • 片段著色器(fragment shader)----可以讓你做更多的效果,但是此shader更難寫。你也可以用它做一些底層的工作,比如頂點(diǎn)光照(Vertex lighting,即在每個(gè)頂點(diǎn)存儲(chǔ)該點(diǎn)的光照信息)。頂點(diǎn)光照對(duì)于移動(dòng)設(shè)備很有用(譯者注:估計(jì)省內(nèi)存吧)。該shader對(duì)于一些需要多通道(multiple passes)的高級(jí)渲染效果也很有效。

本文中我們將關(guān)注點(diǎn)放在表面著色器上。

學(xué)習(xí)Shader的資源

如果你要學(xué)習(xí)Shader編程,我向你推薦下面幾個(gè)資源

  • Martin Kraus‘s fantastic Wiki Book GLSL Programming/Unity
  • Unity‘s Shader Reference
  • NVidia‘s tutorial on the CG programming language
  • CreativeTD‘s video series on writing surface shaders

Shader的流水化工作方式

譯者注:Shader的工作方式也稱為shader流水線(pipeline),因?yàn)閟hader工作方式很類似汽車流水線,將模型上一系列頂點(diǎn)數(shù)據(jù)和其他各種數(shù)據(jù)作為輸入,用這個(gè)shader組成的流水線加工下,出來(lái)的就成了炫酷的效果了。)

你將在shader流水線中看到不明覺(jué)厲的各種術(shù)語(yǔ),我將用我自己的語(yǔ)言盡量降低理解的難度。

Shader的工作就是輸入一些3D幾何信息,經(jīng)過(guò)shader處理后將其變?yōu)?D的像素呈現(xiàn)在屏幕上。好處是在shader處理過(guò)程中,你只需要改變少數(shù)幾個(gè)屬性就可以產(chǎn)生不同的效果。對(duì)于表面著色器,該工作流程看起來(lái)像下面這樣:

(譯者注:簡(jiǎn)單講解一下這個(gè)流程圖,首先要渲染的物體將自己的幾何信息傳遞到Shader中,并且系統(tǒng)得到了該物體的頂點(diǎn)信息,然后你可以選擇經(jīng)不經(jīng)過(guò)Vertex Function來(lái)處理這些頂點(diǎn)信息,隨后經(jīng)過(guò)光柵化(將三維幾何信息映射到二維屏幕上,打個(gè)不恰當(dāng)?shù)谋扔?#xff0c;相當(dāng)于把3D模型拍扁到屏幕上,然后你就可以專心處理屏幕上的像素了),每個(gè)像素經(jīng)過(guò)你的shader代碼將得到最終的顏色值)

注意在表面著色器(Surface Shader)中的函數(shù)退出之前,像素的顏色還沒(méi)有計(jì)算出來(lái)。這意味著你可以再次之前傳入頂點(diǎn)的法向量來(lái)影響光照的計(jì)算。

片段著色器(Fragment Shader)有著同樣的工作流程,但事實(shí)上,片段著色器中必須有Vertex Function(上圖中的Vertex Function部分就是可選的(Optional)),而且需要在像素處理階段做很多的工作才能產(chǎn)生最終的像素。而表面著色器隱藏了這些。(譯者注:給我的感覺(jué)就是片段著色器向用戶提供了更多的接口進(jìn)行更高級(jí)的渲染)。

下圖展示了你的代碼如何被調(diào)用以及代碼構(gòu)成

從上圖我們可以看到,當(dāng)你寫一個(gè)shader的時(shí)候,你可能得有一些屬性值(properties),并且有一個(gè)或多個(gè)Subshaders。具體使用哪個(gè)Subshader進(jìn)行處理取決于你的運(yùn)行平臺(tái)。你應(yīng)該還要指定一個(gè)Fallback shader,當(dāng)你的subshader沒(méi)有一個(gè)能運(yùn)行在你的目標(biāo)設(shè)備上,將使用Fallback shader(譯者注:有點(diǎn)像備胎)

每個(gè)Subshader都至少有一個(gè)通道(pass)作為數(shù)據(jù)的輸入和輸出。你可以使用多個(gè)通道(passes)執(zhí)行不同的操作,比如在一個(gè)Grab Pass中,你可以獲取將要呈現(xiàn)到屏幕上的像素值(譯者注:類似于glsl中的fragment buffer)。當(dāng)你想制作高級(jí)的扭曲效果,這非常有用。雖然當(dāng)你開始學(xué)習(xí)shader編程時(shí),你可能并不會(huì)使用到它。另外一個(gè)使用多通道(multiple passes)的原因是在不同時(shí)刻,你可能需要寫入或者禁止寫入深度緩存的使用。

當(dāng)你寫表面著色器時(shí),我們將直接在Subshader這個(gè)層次上寫代碼,系統(tǒng)將把我們的代碼編譯成若干個(gè)合適的通道(pass)。

盡管shader最終產(chǎn)生的是二維像素,但是其實(shí)這些像素除了保存xy坐標(biāo)外,本身保存著深度值(即每個(gè)像素點(diǎn)上的內(nèi)容在原先3D場(chǎng)景中離照相機(jī)的遠(yuǎn)近),這樣距離照相機(jī)近的物體就會(huì)把距離照相機(jī)遠(yuǎn)的物體遮擋住,在屏幕上顯示時(shí),就是將其像素值覆蓋。

你可以控制是否在你的shader中使用深度緩存(Z-buffer)產(chǎn)生一些特效,或者在Pass中使用一些指令決定shader是否可以寫入Z-buffer:比如使用ZWrite Off時(shí),任何你輸出的東西都不會(huì)更新Z-buffer的值,即關(guān)閉的Z-Buffer的寫入功能。

你可以使用Z-buffer技術(shù)在別的物體上掏出一個(gè)洞,你可以先寫入需要打洞區(qū)域的深度值,但不輸出打洞區(qū)域所屬的像素值,然后在你模型后面的物體的深度值將無(wú)法寫入(因?yàn)閆-buffer覺(jué)得你的模型已經(jīng)擋住了后面的物體)(譯者注:這樣你打洞區(qū)域顯示的就是一開始使用的背景色,會(huì)造成一個(gè)洞穿過(guò)了這些物體的效果)

下面是一些shader代碼:

希望你能看出上面代碼是由PropertiesSubShaderFallback三段代碼組成的。

理解Shader代碼

文章剩下的部分將講述上面那段簡(jiǎn)單代碼到底做了什么?真正的干貨馬上就來(lái)了,你必須好好掌握這些內(nèi)容。

當(dāng)你進(jìn)行shader編程時(shí),你必須使用正確的變量名和函數(shù)名來(lái)調(diào)用它們,事實(shí)上變量的名稱在某些情況下能讓人一眼看出它的特定含義。

創(chuàng)建并使用默認(rèn)Shader

(譯者注:在詳細(xì)介紹Shader之前,我們先簡(jiǎn)單介紹下shader如何使用。)

1.?我們先打開Unity(我的版本是4.6.1),創(chuàng)建新工程,并在Assets文件夾下創(chuàng)建三個(gè)目錄,如下:

2.?我們?cè)賱?chuàng)建一個(gè)cube。

可以在Inspector面板看到新創(chuàng)建的cube所使用的Material如下。

3.?打開Material文件夾,我們?cè)谄渲袆?chuàng)建一個(gè)Shader和一個(gè)Material。

此時(shí)New Material的默認(rèn)Shader為Diffuse。

我們將NewShader拖到New Material上。

可以看到該材質(zhì)所使用的Shader變成我們新建的NewShader了。當(dāng)然你也可以直接點(diǎn)擊材質(zhì)編輯器中Shader下拉框,選擇相應(yīng)的Shader。

4.?最后將New Material拖到cube上。可以看到cube所使用的材質(zhì)和Shader都變成了我們新創(chuàng)建的材質(zhì)和Shader了。

Properties(屬性值)簡(jiǎn)介

你在shader代碼中的Properties{…}部分定義Shader中的屬性值(屬性值就是用戶傳入給shader的數(shù)據(jù),比如紋理之類的,然后shader處理這些紋理,產(chǎn)生特效。可以理解為屬性值相當(dāng)于一種全局變量,而Shader就是那個(gè)主函數(shù),Unity的優(yōu)勢(shì)在于給這個(gè)全局變量賦值可以在Inspector面板進(jìn)行)。注意Properties(屬性值)是所有Subshader代碼中的共享的,意味著所有SubShader代碼中都可以使用這些屬性值。

屬性值(property)定義的形式:

_Name(“Displayed Name”,type) = default value[{options}]

  • _Name?屬性值的名稱,是在shader代碼內(nèi)部使用的,區(qū)別于下面的Displayed Name,后者是在Inspector 面板上顯示的,作為外界(用戶)的輸入提示。
  • Displayed Name?呈現(xiàn)在材質(zhì)編輯器中的屬性值名稱,在Inspector面板上顯示。

總結(jié):打開我們創(chuàng)建的NewShader。可以看到_MainTex是在代碼中使用的,而Base (RGB)是在材質(zhì)編輯器中使用的

  • type?屬性值的類型,包括:
    • Color?– 表示純色,使用了RGBA表示法
    • 2D?– 代表尺寸為2的冪次的紋理(如2,4,8,16…256,512)
    • Rect?– 代表紋理(texture),不同于上面的紋理,此處紋理的大小不一定是2的冪次。
    • Cube?– 用于3d中的cube map,經(jīng)常提到的天空盒就是使用了cube map。
    • Range(min, max)?– 在min和max之間的一個(gè)值,在面板中可用滑動(dòng)條改變其值大小。
    • Float?– 任意一浮點(diǎn)數(shù)。
    • Vector?– 4維向量值,本質(zhì)就是4個(gè)浮點(diǎn)數(shù)組成的類型。

來(lái)張全家福:

  • default value?屬性值的初始值,就相當(dāng)于你的變量初始化的那個(gè)值。
    • Color – (red,green,blue,alpha)?使用了RGBA這種格式的顏色,alpha指的是透明度– 比如 (1,1,1,1)
    • 2D/Rect/Cube?– 紋理的類型,上面已經(jīng)介紹過(guò)了。初始化值可以使一個(gè)空字符串,或者"white", "black", "gray", "bump"(說(shuō)明此紋理是一個(gè)凹凸紋理)
    • Float/Range?– 這個(gè)沒(méi)啥說(shuō)的,跟浮點(diǎn)數(shù)初始化一樣一樣的
    • Vector – 4維向量,其中4個(gè)數(shù)均為浮點(diǎn)數(shù) (x,y,z,w)
  • { options }?這里注意了,{options}?僅僅用于紋理類型,比如上面提到的2DRectCube,對(duì)于這些類型,如果沒(méi)有options可填,至少要寫一個(gè)空的{},否則編譯出錯(cuò)。可以使用空格將多個(gè)options(選項(xiàng))分開 ,可用的options(選項(xiàng))如下:
    • ?TexGen?texgenmode紋理坐標(biāo)自動(dòng)生成的方式。可以是ObjectLinear,?EyeLinear,?SphereMap,?CubeReflect,CubeNormal其中之一,這些方式和OpenGL中的紋理坐標(biāo)生成方式相對(duì)應(yīng),具體詳見這篇博文。注意當(dāng)你寫Vertex Function時(shí),紋理坐標(biāo)產(chǎn)生方式將被忽略。

下面舉幾個(gè)屬性值寫法的例子:

// 定義了一個(gè)半透明(alpha=0.5)效果的紅色作為默認(rèn)顏色值_MainColor(“Main Color”,Color)=(1,0,0,0.5)// 定義了一個(gè)默認(rèn)值為白色的紋理_Texture(“Texture”,2D) =”white” {}

注意屬性值的定義末尾處不需添加分號(hào)。

標(biāo)簽(Tags)

你的表面著色器可以用一個(gè)或多個(gè)標(biāo)簽(tags)進(jìn)行修飾。這些標(biāo)簽的作用是告訴硬件何時(shí)去調(diào)用你的shader代碼。

在我們的例子中,我們使用:Tags {“RenderType” = “Opaque”},這意味著當(dāng)程序去渲染不透明的幾何體時(shí),將調(diào)用我們的shader,Unity定義了一系列這樣的渲染過(guò)程。另一個(gè)很容易理解的標(biāo)簽就是Tags {“RenderType” = “Transparent”},意味著我們的shader只會(huì)輸出半透明或透明的像素值。

其它一些有用的標(biāo)簽,比如“IgnoreProjector”=“True”,意味著你渲染的物體不會(huì)受到projectors(投影儀)的影響。

“Queue”=“xxxx”(給shader所屬的對(duì)象貼上渲染隊(duì)列的標(biāo)簽)。當(dāng)渲染的對(duì)象類型是透明物體時(shí),Queue標(biāo)簽?zāi)墚a(chǎn)生一些非常有趣的效果。該標(biāo)簽決定了物體渲染的順序(譯者注:我猜測(cè)它的工作方式是這樣的,一個(gè)場(chǎng)景中有很多個(gè)物體,當(dāng)這些物體被渲染時(shí),必須有一個(gè)渲染的順序,比如背景應(yīng)該比其他物體先渲染出來(lái),否則背景會(huì)將之前渲染的物體遮擋住,具體方法是將背景使用的shader中貼上一個(gè)“Queue”=“Backfround”標(biāo)簽,這樣使用該shader的物體將被貼上Background的標(biāo)簽。總之當(dāng)渲染整個(gè)場(chǎng)景時(shí),unity會(huì)根據(jù)這些渲染隊(duì)列的標(biāo)簽決定按什么順序去渲染對(duì)應(yīng)標(biāo)簽所屬的物體)。

  • Background?– 在所有其他物體渲染之前渲染,被用于天空盒或類似的背景效果。
  • Geometry(默認(rèn)tags為geometry)?– 適用于大多數(shù)物體。非透明物體使用這種渲染順序。
  • AlphaTest?– 順利通過(guò)alpha測(cè)試的像素(alpha-test是指當(dāng)前像素的alpha小于一定的值就舍棄該像素)使用該渲染順序。單獨(dú)設(shè)置該渲染順序是因?yàn)樵谒袑?shí)體渲染過(guò)后,該渲染順序?qū)?duì)渲染經(jīng)過(guò)alpha測(cè)試的物體更有效率。
  • Transparent?– 該渲染標(biāo)簽所屬的物體將在標(biāo)簽為Geometry和AlphaTest之后的物體渲染,并且這些貼著Transparent的所有物體本身是從后往前依次渲染的。任何經(jīng)過(guò)alpha-blended的物體都應(yīng)該使用該標(biāo)簽(譯者注:alpha-blended是指使用當(dāng)前像素的alpha作為混合因子,來(lái)混合之前寫入到緩存中像素值,此時(shí)注意shader是不能寫入深度緩存的,因?yàn)槿绻魂P(guān)閉寫入深度緩存,那么在進(jìn)行深度檢測(cè)的時(shí)候,它背后的物體本來(lái)我們是可以透過(guò)它被我們看到的,但由于深度檢測(cè)時(shí),小于它的深度就被剔除了,從而我們就看不到它后面的物體了),玻璃和粒子效果比較適合該渲染標(biāo)簽。
  • Overlay?– 該渲染標(biāo)簽適合覆蓋效果,任何最后渲染的效果都可以使用該標(biāo)簽,比如透鏡光暈。

有趣的是你可以給這些基本的渲染標(biāo)簽進(jìn)行加加減減。這些預(yù)定義的值本質(zhì)上是一組定義整數(shù),Background = 1000, Geometry = 2000, AlphaTest = 2450, Transparent = 3000,最后Overlay = 4000(譯者注:從此處我們也可以一窺究竟,貌似數(shù)值大的后渲染。)這些預(yù)設(shè)值這對(duì)透明物體有很大影響,比如一個(gè)湖水的平面覆蓋了你用廣告牌制作的樹,你可以對(duì)你的樹使用“Queue”=”Transparent-102”,這樣你的樹就會(huì)繪制在湖水前面了。

Shader的整體結(jié)構(gòu)

讓我們回顧下shader代碼的結(jié)構(gòu)。

#pragma surface surf Lambert?這段代碼表示其中surface表示這是一個(gè)表面著色器,進(jìn)行結(jié)果輸出的函數(shù)名稱為surf,其使用的光照模型為L(zhǎng)ambert光照模型。

我們的CG程序使用了一種經(jīng)過(guò)修飾的類C語(yǔ)言 —— CG語(yǔ)言(是Nvidia和微軟共同出品的一種shader語(yǔ)言)。詳見Nvidia的文檔?—— 我在文中也會(huì)介紹一些基本的Cg使用方法。

浮點(diǎn)數(shù)類型(float)和向量值類型(vec)一般都會(huì)在末尾加上2,3,4這些數(shù)字(float2,float4,vec3…)表示該類型具體有幾個(gè)元素組成。這種定義方式使數(shù)值操作變得更方便,你可以將其當(dāng)做一個(gè)整體使用,或者單獨(dú)使用其分量。

//定義一個(gè)浮點(diǎn)類型的二維坐標(biāo)vec2 coordinate;//定義一個(gè)顏色變量(4個(gè)浮點(diǎn)值分量的顏色值)float4 color;//通過(guò)點(diǎn)乘得到3個(gè)浮點(diǎn)值分量的顏色值float3 multipliedColor = color.rgb * coordinate.x;

你可以使用.xyzw或.rgba來(lái)表明你使用的變量類型具體的含義,比如.xyzw可能表示的是旋轉(zhuǎn)四元數(shù),而.xyz表示位置或法向量,.rgba表示顏色。當(dāng)然,你可以僅僅使用float作為單個(gè)浮點(diǎn)值類型。其實(shí)對(duì).rgba等分量訪問(wèn)符的使用也稱作swizzle,尤其是對(duì)顏色的處理,比如顏色空間的轉(zhuǎn)換可能會(huì)用到它,比如color=color.abgr;

你將會(huì)遇到half(半精度)和double(雙精度)類型,half(一般16bit)即正常float(一般32bit)的一半精度,double(一般64bit)是正常float的兩倍精度(此處的倍數(shù)衡量的方式不是指表示的范圍,而是表示可以使用的bit位數(shù))。使用half經(jīng)常是出于性能考慮的原因。還有一種區(qū)別于浮點(diǎn)數(shù)的定點(diǎn)數(shù)fixed,精度更低。

當(dāng)你想將顏色值規(guī)范到0~1之間時(shí),你可能會(huì)想到使用saturate函數(shù)(saturate(x)的作用是如果x取值小于0,則返回值為0。如果x取值大于1,則返回值為1。若x在0到1之間,則直接返回x的值.),當(dāng)然saturate也可以使用變量的swizzled版本,比如saturate(somecolor.rgb);

你可以使用length函數(shù)得到一個(gè)向量的長(zhǎng)度,比如float size = length(someVec4.xz);

如何從表面著色器輸出信息

我們的surface function(表面函數(shù))每個(gè)像素調(diào)用一次,系統(tǒng)已經(jīng)事先計(jì)算出當(dāng)前處理的像素的輸入值(準(zhǔn)確來(lái)說(shuō)應(yīng)該是輸入結(jié)構(gòu)體,即Input IN中的Input類型)。 它是根據(jù)每個(gè)網(wǎng)格上的面片,并進(jìn)行插值得到的結(jié)果。

來(lái)看看我們的surf函數(shù)

void surf (Input IN, inout SurfaceOutput o) {o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;}

很明顯我們可以看出,我們返回了o.Albeodo值 – 該值是Unity為我們定義的SurfaceOutput結(jié)構(gòu)體中的某個(gè)成員。接下來(lái)讓我們看看SurfaceOutput具體定義了哪些成員。該Albedo表示像素的顏色。

struct SurfaceOutput {half3 Albedo; //該像素的顏色值 half3 Normal; //該像素的法向量 half3 Emission; //該像素的輻射光,輻射光是最簡(jiǎn)單的一種光,它直接從物體發(fā)出并且不受任何光源影響 half Specular; //該像素的鏡面高光 half Gloss; //該像素的發(fā)光強(qiáng)度 half Alpha; //該像素的透明度 };

你只要將該結(jié)構(gòu)體中值交給Unity,Unity會(huì)自動(dòng)根據(jù)這些值產(chǎn)生最終效果,而不需要你關(guān)心其中的細(xì)節(jié)。

我答應(yīng)你們的干貨就在下面

首先看看作為我們surf函數(shù)的輸入是啥?

我們定義了一個(gè)輸入結(jié)構(gòu)體如下:

struct Input {float2 uv_MainTex;};

通過(guò)簡(jiǎn)單地創(chuàng)建結(jié)構(gòu)體,我們告訴系統(tǒng)當(dāng)我們每次調(diào)用surf函數(shù)時(shí),獲取MainTex在該像素的紋理坐標(biāo)。如果我們有第二個(gè)紋理叫做—_OtherTexture,我們可以通過(guò)在輸入結(jié)構(gòu)體中添加下面代碼得到它的紋理坐標(biāo)

struct Input {float2 uv_MainTex;float2 uv_OtherTexture;};

如果對(duì)于其他紋理,我們有第二套紋理坐標(biāo),我們可以這樣做:

struct Input {float2 uv_MainTex;float2 uv2_OtherTexture;};

此時(shí)對(duì)于我們所使用的所有紋理,我們的輸入結(jié)構(gòu)體包含一套u(yù)v坐標(biāo)或者一套u(yù)v2坐標(biāo)。

如果我們的shader很復(fù)雜并且需要知道像素的其他相關(guān)信息,我們就可以將以下變量包含在輸入結(jié)構(gòu)體中,以此來(lái)查詢其他的相關(guān)變量。

  • float3?viewDir?–?視圖方向( view direction)值。為了計(jì)算視差效果(Parallax effects),邊緣光照(rim lighting)等,需要包含視圖方向(view direction)值。
  • float4? with COLOR semantic(比如float4 currentColor,即用戶自定義和顏色相關(guān)的變量名稱) – 每個(gè)頂點(diǎn)(per-vertex)顏色的插值。
  • float4 screenPos?– 為了反射效果,需要包含屏幕空間中的位置信息
  • float3 worldPos?– 世界空間中的位置
  • float3 worldRefl?– 世界空間中的反射向量。如果表面著色器(surface shader) 不為SurfaceOutput結(jié)構(gòu)中的Normal賦值,將包含這個(gè)參數(shù)。
  • float3 worldNormal?– 世界空間中的法線向量(normal vector)。如果表面著色器(surface shader) 不為SurfaceOutput結(jié)構(gòu)中的Normal賦值,將包含這個(gè)參數(shù)。
  • INTERNAL_DATA?– 相對(duì)于上面的float3 worldRefl和float3 worldNormal,如果表面著色器為SurfaceOutput結(jié)構(gòu)中的Normal賦值了,將使用該參數(shù)。為了獲得基于每個(gè)頂點(diǎn)法線貼圖( per-pixel normal map)的反射向量(reflection vector)需要使用世界反射向量(WorldReflectionVector (IN, o.Normal)),其中o.Normal表示的是切空間的法向量,而非世界坐標(biāo)系下的法向量。
  • 你可能會(huì)問(wèn)上面的COLOR semantic是什么意思?當(dāng)你寫一個(gè)正常的片段著色器時(shí),你得告訴別人你的輸入結(jié)構(gòu)體每個(gè)變量代表什么意思?如果你夠瘋狂,你可以試試下面這樣定義:float2 MyUncleFred : TEXCOORD0; 并告訴別人MyUncleFred表示該模型的uv坐標(biāo)。(畫外音就是這種變量命名方式很令人費(fèi)解)在表面著色器中你唯一擔(dān)心的就是對(duì)COLOR類型的定義。float4 currentColor : COLOR;可以看做目前已經(jīng)經(jīng)過(guò)插值后的像素顏色。當(dāng)然你也可以不用關(guān)心這些,不過(guò)建議你命名上最好規(guī)范些,方便自己也方便別人。

該shader實(shí)際做了哪些事?

現(xiàn)在我們還有兩行代碼沒(méi)有詳細(xì)討論:

Sampler2D _MainTex;

對(duì)每一個(gè)屬性值,我們定義了屬性值區(qū)域(Properties Section),該區(qū)域用來(lái)定義CG程序中使用的變量。在使用中,我們必須保證屬性名稱一致。

注意輸入結(jié)構(gòu)體中的uv_MainTex是uv+對(duì)應(yīng)屬性值(文中為_MainTex,注意前面帶下劃線是CG官方推薦的寫法),如果你使用uv2,那將寫作uv2_MainTex。注意Sampler2D _MainTex中的_MainTex變量是一個(gè)Sampler2D(這個(gè)Sampler2D,可以理解為引用一個(gè)2D Texture),它引用了Properties中的_MainTex(譯者注:注意兩者同名。解釋通了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è)變量。),他可以根據(jù)指定的uv坐標(biāo)來(lái)提供對(duì)應(yīng)紋理上的像素值,而此處uv_MainTex的作用就是提供紋理_MainTex的uv坐標(biāo)值。

如果我們定義了一個(gè)_Color變量,我們可以定義它的屬性為

float4 _Color;

我們surf函數(shù)中唯一一行代碼

o.Albedo = tex2d( _MainTex, IN.uv_MainTex).rgb;

tex2d的作用是利用IN.uv_MainTex所代表的uv坐標(biāo)(注意我們上面指定了uv坐標(biāo)產(chǎn)生的方式,所以此處的IN.uv_MainTex是自動(dòng)生成的)對(duì)紋理_MainTex進(jìn)行采樣。此處,對(duì)于o.Albedo我們只取顏色分量中的rgb三分量,其中alpha值(透明度)目前不需要,至少對(duì)于非透明物體alpha值得作用不大。

如果你要設(shè)置alpha值的話,可以像下面這樣賦值

float4 texColor = tex2d( _MainTex, IN.uv_MainTex );o.Albedo = texColor.rgb;o.Alpha = texColor.a;

總結(jié)

你已經(jīng)了解了很多的術(shù)語(yǔ),但是目前我們所寫的shader還相當(dāng)有限,但是當(dāng)學(xué)習(xí)完第二部分教程后,我們就可以做一些很酷炫的shader了,因?yàn)榈诙糠治覀儗㈤_始使用多重紋理,法向量等等酷炫技術(shù)。

  • 在第二部分中,我們創(chuàng)建了一個(gè)實(shí)現(xiàn)積雪效果的shader,根據(jù)積雪的程度來(lái)修改模型,以呈現(xiàn)不同效果。
  • 在第三部分我們改進(jìn)了我們的shader來(lái)混合巖石邊緣的積雪。
  • 在第四部分,我們使用黑色邊緣和漸變紋理來(lái)創(chuàng)建了具有卡通效果的shader。
  • 在第五部分,我們創(chuàng)建了一個(gè)頂點(diǎn)/片段多通道凹凸紋理著色器(vertex/fragment multipass bumped shader) – 其復(fù)雜程度遠(yuǎn)遠(yuǎn)超越表面著色器
  • 在第六部分,我們創(chuàng)建了一個(gè)頂點(diǎn)/片段著色器(vertex/fragment shader)來(lái)制作相比于我們第四部分使用表面著色器制作的卡通效果shader更好的shader。

總結(jié)

以上是生活随笔為你收集整理的【译】Unity3D Shader 新手教程(1/6)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。