Unity shader 官網(wǎng)文檔全方位學(xué)習(xí)(一)

Unity shader 官網(wǎng)文檔全方位學(xué)習(xí)(一)
What?? Shader,看起來(lái)好高級(jí)的樣子,是的,這是Unity中高級(jí)進(jìn)階的必備。因此,兄弟我就在此記下我學(xué)習(xí)官網(wǎng)的一些心得。
此為一。主要介紹些Surface Shaders的知識(shí)。具體的大家也可去官網(wǎng)(如下)學(xué)習(xí)。
http://docs.unity3d.com/Documentation/Components/SL-SurfaceShaders.html
一、概念篇
1.基準(zhǔn):unity里的shader并不是一門獨(dú)特的語(yǔ)言,而是一種代碼生成方式,且可將低層次且復(fù)雜的shader編程進(jìn)行簡(jiǎn)化。但同時(shí)你也還是得使用Cg/HLSL來(lái)寫的。
2.原理:寫一個(gè)函數(shù),以UVs或者一些數(shù)據(jù)為入口,然后以SurfaceOutput為輸出。同時(shí)在SurfaceOutput這個(gè)結(jié)構(gòu)體里還有不同的屬性。這樣對(duì)于這個(gè)函數(shù)來(lái)說(shuō),他的執(zhí)行過(guò)程會(huì)生成vertex和pixel的Shader,并且傳遞一些渲染的路徑。
3.結(jié)構(gòu):輸出結(jié)構(gòu):
struct SurfaceOutput { half3 Albedo; half3 Normal; half3 Emission; half Specular; half Gloss; half Alpha;};

Albedo,是漫反射的顏色值。Normal,法線坐標(biāo)Emission,自發(fā)光顏色Specular,鏡面反射系數(shù)Gloss,光澤系數(shù)Alpha,透明度系數(shù)
二、編程規(guī)則
1.要寫在CGPROGRAM..ENDCG的SubShader的塊里。不可寫在Pass里。
2.shader的名字是可以重復(fù)的,重復(fù)后,以后來(lái)的shader為主。
3.指令詳細(xì):

pragma surface surfaceFunction lightModel [optionalparams]

=>surfaceFunction,沒(méi)什么好說(shuō),肯定是函數(shù)名了。
=>lightModel是所采用的光照模型。可以自己寫也可使用內(nèi)置如Lambert和BlinnPhong.
=>optionalparams:可選參數(shù),一堆可選包括透明度,頂點(diǎn)與顏色函數(shù),投射貼花shader等等。具體用到可以細(xì)選。
另外這里有一個(gè)功能。在Surface shader的CGPROGRAM里添加 #pragma debug [內(nèi)容]??稍诰幾g結(jié)果的文件中看到。寫多少都行。但嘗試在其他種shader下不行。
三、實(shí)例學(xué)習(xí):
1.Simple:
Shader "Example/Diffuse Simple" { SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Lambert struct Input { float4 color : COLOR; }; void surf (Input IN, inout SurfaceOutput o) { o.Albedo = 1; } ENDCG } Fallback "Diffuse" }

第一個(gè)。行行來(lái):第一行:寫個(gè)名字。這也有講究的。斜線左邊為其父類的組,無(wú)則新增,有則累加,右邊才是真正的名字。注意,這些shader名不像C#腳本,無(wú)需文件名與shader名相同。
第二、三行:接下來(lái)就在SubShader里添加內(nèi)容,SubShader是可以有多個(gè)的。然后上一個(gè)Tags,此處只用到RenderType這種,另外的還有Rendering order, ForceNoShadowCasting..等。這些本階段暫不研究。
第四行:上一條指令,里面指定響應(yīng)方法為surf且采用Lambert的光照模型。這個(gè)必須有的。
第五行:這個(gè)結(jié)構(gòu)體,記得名字不能改,只能為Input。里面一個(gè)四元素的顏色值(RGBA)。
第七到第九行:第一個(gè)參數(shù),純輸入的上述結(jié)構(gòu)體參數(shù)。第二個(gè)參數(shù),inout標(biāo)識(shí),意思是可為輸入?yún)?shù)也可為輸出參數(shù)。Albedo根據(jù)前面介紹到的,是一個(gè)rgb的值,如果給一個(gè)1,其實(shí)就是float3(1,1,1),就是反射出來(lái)的顏色為白色,如果為100,則是加強(qiáng)反射強(qiáng)度,并不會(huì)改變其顏色。為0或?yàn)樨?fù)數(shù)時(shí)道理類似。
最后Fallback,后方的是自帶的shader,可以用自己自定義好的。這里這句的意思是,如果所有subshader在當(dāng)前顯卡都不支持,則默認(rèn)返回自帶的Diffuse。


2.Texture:
Shader "Example/Diffuse Texture" { ** Properties {** _MainTex ("Texture", 2D) = "white" {} } SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Lambert struct Input { float2 uv_MainTex; }; ** sampler2D _MainTex;** void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; } ENDCG } Fallback "Diffuse" }

這個(gè)例子呢。其實(shí)只是第一個(gè)的基礎(chǔ)上添加了一個(gè)2D屬性顯示名為Texture。以下解析:第一個(gè)黑體:添加一個(gè)名叫_MainTex的屬性,指定其為2D類型且顯示為Texture。"white"那塊可不是亂寫的,是unity的build-in的一些textures的名稱,而不是單純顏色名字。意思是當(dāng)默認(rèn)時(shí)顯示為名叫white的材質(zhì)。如改成red(即使用名叫red的材質(zhì),如果有其他也可叫其名字),則效果如下:



第二個(gè)黑體:uv_MainTex。這其中大有玄機(jī),uv開(kāi)頭指代后方材質(zhì)的uv值,因此uv不變,后面的可以根據(jù)開(kāi)頭起的名字動(dòng)態(tài)換。還有哦,這種類似于_MainTex的命名方式是CG推薦的,其實(shí)不用下劃線也OK的。
第三個(gè)黑體:這個(gè)Sampler2D,可以理解為引用一個(gè)2D Texture。因?yàn)橄旅娴腡ex2D函數(shù)需要這種類型。所以說(shuō)這個(gè)后面的名字要與Properties里的對(duì)應(yīng)一樣才行。
第四個(gè)黑體:Tex2D,這玩意就是根據(jù)對(duì)應(yīng)材質(zhì)上所有的點(diǎn)找指定 2DSample上的Texture信息,此處需要其RGB信息,就打出來(lái)賦給了其反射值。所以對(duì)有材質(zhì)圖的情況下,要顯示出圖,還是要相應(yīng)的反射其原圖的rgb值。



3.Normal mapping
Shader "Example/Diffuse Bump" { Properties { _MainTex ( "Texture", 2D) = "white" {} ** _BumpMap ("Bumpmap", 2D) = "bump" {}** } SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Lambert struct Input { float2 uv_MainTex; float2 uv_BumpMap; }; sampler2D _MainTex; ** sampler2D _BumpMap;** void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; ** o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));** } ENDCG } Fallback "Diffuse" }

這個(gè)例子里加了個(gè)凹凸貼圖,可實(shí)現(xiàn)類似一些很漂亮的凹凸效果。第一個(gè)黑體:加一個(gè)2D類型的材質(zhì),默認(rèn)為bump。(即帶有凹凸效果的)。
第二個(gè)黑體:上一個(gè)采集器。采集下來(lái)上面的材質(zhì)。
第三個(gè)黑體:有講究,這個(gè)UnpackNormal是unity自帶的標(biāo)準(zhǔn)解壓法線用的,所謂解壓,我暫時(shí)學(xué)習(xí)到的只是將法線的區(qū)間進(jìn)行變換。由于tex2D(_BumpMap, IN.uv_BumpMap)取出的是帶壓縮的[0,1]之間,需要轉(zhuǎn)成[-1,1]。這個(gè)函數(shù)會(huì)針對(duì)移動(dòng)平臺(tái)或OPENGL ES平臺(tái)采用 RGB法線貼圖,其他采用DXT5nm貼圖。為此也可自己寫。也在網(wǎng)上找到了一些資料,如下參考:
// Shader: 帶法線貼圖的Surface Shader// Author: 風(fēng)宇沖Shader "Custom/3_NormalMap" { Properties { _MainTex ("Texture", 2D) = "white" {} _NormalMap ("NormalMap", 2D) = "white" {} } Subshader { CGPROGRAM #pragma surface surf BlinnPhong struct Input { float2 uv_MainTex; }; //法線范圍轉(zhuǎn)換:?jiǎn)挝环ň€ float3(x,y,z),x,y,z的取值范圍是 [-1,1]。在法線貼圖中被壓縮在顏色的范圍[0,1]中,所以需要轉(zhuǎn)換 //(1)RGB法線貼圖 float3 expand(float3 v) { return (v - 0.5) * 2; } //(2)DXT5nm法線貼圖 float3 expand2(float4 v){fixed3 normal;normal.xy = v.wy * 2 - 1;normal.z = sqrt(1 - normal.x*normal.x - normal.y * normal.y);return normal;} sampler2D _MainTex; sampler2D _NormalMap; void surf(Input IN,inout SurfaceOutput o) { half4 c = tex2D(_MainTex, IN.uv_MainTex); o.Albedo = c.rgb; o.Alpha = c.a;//對(duì)法線貼圖進(jìn)行采樣,取得壓縮在顏色空間里的法線([0,1]) float4 packedNormal = tex2D(_NormalMap, IN.uv_MainTex); //要將顏色空間里的法線[0,1],轉(zhuǎn)換至真正3D空間里的法線范圍[-1,1] //注意:范圍基本都是從[0,1]轉(zhuǎn)換至[-1,1].主要是圖的通道與法線xyz的對(duì)應(yīng)關(guān)系要根據(jù)法線貼圖格式而定 //UnpackNormal, UnityCG.cginc里的函數(shù) //o.Normal = UnpackNormal(packedNormal); //expand,標(biāo)準(zhǔn)法線解壓函數(shù) o.Normal = expand(packedNormal.xyz); } ENDCG }}

[轉(zhuǎn)載自http://bbs.9ria.com/thread-169460-1-1.html]


4.Rim Lighting
Shader "Example/Rim" { Properties { _MainTex ("Texture", 2D) = "white" {} _BumpMap ("Bumpmap", 2D) = "bump" {} ** _RimColor ("Rim Color", Color) = (0.26,0.19,0.16,0.0)//1 ** _RimPower ("Rim Power", Range(0.5,8.0)) = 3.0 //2 ** } SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Lambert struct Input { float2 uv_MainTex; float2 uv_BumpMap; float3 viewDir; //3 ** }; sampler2D _MainTex; sampler2D _BumpMap; ** float4 _RimColor;//4 ** float _RimPower;//5 void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap)); ** half rim = 1.0 - saturate(dot (normalize(IN.viewDir), o.Normal));//6** ** o.Emission = _RimColor.rgb * pow (rim, _RimPower);//7** } ENDCG } Fallback "Diffuse" }

新增的一些東西,我都用數(shù)字標(biāo)注了。以下進(jìn)行詳細(xì)解讀:
第一處(//1):上一個(gè)Color類型的顯示為Rim Color的變量。顏色值RGBA對(duì)應(yīng)0.26,0.19,0.16,0.0
第二處(//2):這個(gè)Range類型的變量,結(jié)果還是一個(gè)float。只是這個(gè)float是在這個(gè)range之內(nèi)。為什么這么定義呢。如果超多,或過(guò)小,則使用range內(nèi)指明的值代替。
第三處(//3):viewDir 意為World Space View Direction。就是當(dāng)前坐標(biāo)的視角方向。這里有個(gè)從相關(guān)網(wǎng)上找的圖:鏈接:http://game.ceeger.com/forum/read.php?tid=11367


第四、五處(//4,//5):定義兩個(gè)變量對(duì)應(yīng)properties里的值,取出使用。
第六、七處:最里層是Normalize函數(shù),用于獲取到的viewDir坐標(biāo)轉(zhuǎn)成一個(gè)單位向量且方向不變,外面再與點(diǎn)的法線做點(diǎn)積。最外層再用saturate算出一[0,1]之間的最靠近(最小值但大于所指的值)的值。這樣算出一個(gè)rim邊界。為什么這么做。原理以下解釋:
=>看圖。

=>這里o.Normal就是單位向量。外加Normalize了viewDir。因此求得的點(diǎn)積就是夾角的cos值。
=>因?yàn)閏os值越大,夾角越小,所以,這時(shí)取反來(lái)。這樣,夾角越大,所反射上的顏色就越多。于是就得到的兩邊發(fā)光的效果。哈哈這樣明了吧。
這里介紹一下這個(gè)half。CG里還有類似的float和fixed。half是一種低精度的float,但有時(shí)也會(huì)被選擇成與float一樣的精度。fragment是一定會(huì)支持fixed類型,同時(shí)也會(huì)有可能將其精度設(shè)成與float一樣,這個(gè)比較復(fù)雜,后面篇章學(xué)到fragment時(shí)再深入探討。
以下為與3的對(duì)比,大家一下就知道誰(shuí)是用了rim color的吧。對(duì)!下面那個(gè)盒子就是用些shader的效果。

5.Detail Texture
Shader "Example/Detail" { Properties { _MainTex ("Texture", 2D) = "white" {} _BumpMap ("Bumpmap", 2D) = "bump" {} ** _Detail ("Detail", 2D) = "gray" {}** } SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Lambert struct Input { float2 uv_MainTex; float2 uv_BumpMap; ** float2 uv_Detail;** }; sampler2D _MainTex; sampler2D _BumpMap; ** sampler2D _Detail;** void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; ** o.Albedo = tex2D (_Detail, IN.uv_Detail).rgb * 2;* o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap)); } ENDCG } Fallback "Diffuse" }

這個(gè)最好理解了。前面三個(gè)一樣。上一個(gè)2D Texture。
最后一個(gè)黑體:在原先的反射基礎(chǔ)上,在加一層,Texture的反射。
就是這樣啦。最后上幾個(gè)截圖,大家一定就明白。



6.Detail Texture in Screen Space
Shader "Example/ScreenPos" { Properties { _MainTex ("Texture", 2D) = "white" {} _Detail ("Detail", 2D) = "gray" {} } SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Lambert struct Input { float2 uv_MainTex; float4 screenPos; }; sampler2D _MainTex; sampler2D _Detail; void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; ** float2 screenUV = IN.screenPos.xy / IN.screenPos.w; //2 ** **screenUV *= float2(8,6); ** *o.Albedo = tex2D (_Detail, screenUV).rgb * 2; } ENDCG } Fallback "Diffuse" }

這個(gè)比較有趣,是從上個(gè)例子的基礎(chǔ)上將第二層疊加上的2D Texture根據(jù)當(dāng)前屏幕的UV進(jìn)行疊加,而不是根據(jù)自身的UV。這樣帶有含此shader材質(zhì)的物體的貼圖就會(huì)跟著移動(dòng)到的位置而變換圖片。這里只需要說(shuō)三點(diǎn):
1.關(guān)于screenPos:screenPos是一個(gè)三維點(diǎn),但是用齊次坐標(biāo)的形式表示出來(lái)就是(x,y,z,w),根據(jù)齊次坐標(biāo)的性質(zhì)。(x,y,z,w)的齊次坐標(biāo)對(duì)應(yīng)三維點(diǎn)(x/w,y/w,z/w)。因此把w值除掉可以看來(lái)是一種Normalize的作法,這樣就取出了實(shí)際的屏幕xy的UV值。
2.對(duì)screenUV進(jìn)行倍剩:此處剩float2(8,6)意為將原獲取到屏幕尺寸進(jìn)行拉大的倍數(shù)。即x軸拉大8倍,y軸拉大6倍。
3.如何就平鋪了剛好一行8個(gè),一列6個(gè)了呢? 原因我覺(jué)得是在于2d Texture自己是按Normalize后進(jìn)行鋪的,因此在//2(剛轉(zhuǎn)完標(biāo)準(zhǔn)的)screenPos后,將其剩多少即便將原圖鋪多少?gòu)垺?br> OK。明了。其實(shí)這個(gè)東西可以拿來(lái)做放大鏡的應(yīng)用。上圖:


  1. Cubemap reflection
    Shader "Example/WorldRefl" { Properties { _MainTex ( "Texture", 2D) = "white" {} ** _Cube ("Cubemap", CUBE) = "" {}** } SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Lambert struct Input { float2 uv_MainTex; ** float3 worldRefl;** }; sampler2D _MainTex; ** samplerCUBE _Cube;** void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb * 0.5; ** o.Emission = texCUBE (_Cube, IN.worldRefl).rgb;** } ENDCG } Fallback "Diffuse" }

Shader "Example/WorldRefl Normalmap" { Properties { _MainTex ("Texture", 2D) = "white" {} _BumpMap ("Bumpmap", 2D) = "bump" {} _Cube ("Cubemap", CUBE) = "" {} } SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Lambert struct Input { float2 uv_MainTex; float2 uv_BumpMap; float3 worldRefl; INTERNAL_DATA }; sampler2D _MainTex; sampler2D _BumpMap; samplerCUBE _Cube; void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb * 0.5; o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap)); o.Emission = texCUBE (_Cube, WorldReflectionVector (IN, o.Normal)).rgb; } ENDCG } Fallback "Diffuse" }

這兩段都是加一個(gè)cubemap的反射。第二段相比之下是在有normal反射的基礎(chǔ)上加的。Cubemap這東西,可設(shè)置幾種面的不能渲染圖,這方面可用于做天空盒。因?yàn)檫@樣可以從各個(gè)角度看過(guò)去以顯示不同的渲染效果。以下說(shuō)明:

  1. worldRefl:即為世界空間的反射向量。
  2. texCUBE:將反射向量一個(gè)個(gè)的往_Cube反射盒上找出然后做為Emission反射出來(lái)。
  3. 第二個(gè)例子只是將其用在Normal反射后,這樣一定要多添加一個(gè)INTERNAL_DATA的屬性,另外也需用到WorldReflectionVectore方法取其利用Normal后的反射向量值。
    類似于的效果,可見(jiàn)官網(wǎng)中的。我這也有一個(gè),有點(diǎn)像打了光的樣子。

    8.Slices via World Space Position
    Shader "Example/Slices" { Properties { _MainTex ("Texture", 2D) = "white" {} _BumpMap ("Bumpmap", 2D) = "bump" {} } SubShader { Tags { "RenderType" = "Opaque" } ** Cull Off ** CGPROGRAM #pragma surface surf Lambert struct Input { float2 uv_MainTex; float2 uv_BumpMap; ** float3 worldPos;** }; sampler2D _MainTex; sampler2D _BumpMap; void surf (Input IN, inout SurfaceOutput o) { ** clip (frac((IN.worldPos.y+IN.worldPos.z0.1) * 5) - 0.5);* o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap)); } ENDCG } Fallback "Diffuse" }

在看完這段后,我自己另外又加一段,以作對(duì)比:
float3 _tWorldPos;void surf (Input IN, inout SurfaceOutput o) { _tWorldPos = IN.screenPos.xyz / IN.screenPos.w; //clip (frac((IN.worldPos.y+IN.worldPos.z0.1) * 5) - 0.5); clip (frac((_tWorldPos.y+_tWorldPos.z0.1) * 3) - 0.5); o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));}

第二個(gè)黑體:frac是取小數(shù)的函數(shù),如1.23 取出來(lái)是 0.23。clip函數(shù)用于清Pixel的,負(fù)值情況下才進(jìn)行清pixel。且越小,即絕對(duì)值越大則清越多。 這里注意那個(gè)* 5,仔細(xì)一想,如果frac出來(lái)的值越大,-0.5值就越大,絕對(duì)值就越小,因此這樣清掉的pixel越少,所以就可以間接的增加分段的次數(shù)。那為什么要+IN.worldPos.z*0.1呢,主要原因就是空開(kāi)的斷添加一個(gè)傾斜角度,可以用空間思想想下。我的那段,就是將要clip的坐標(biāo)換掉,換成屏幕的。這樣你移動(dòng)物體時(shí),clip掉的部分會(huì)變化。
最后,上下效果圖:



9.Normal Extrusion with Vertex Modifier

Shader "Example/Normal Extrusion" { Properties { _MainTex ("Texture", 2D) = "white" {} ** _Amount ("Extrusion Amount", Range(-1,1)) = 0.5** } SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Lambert vertex:vert //1 struct Input { float2 uv_MainTex; }; ** float _Amount;** //2 ** void vert (inout appdata_full v) { //3 ** v.vertex.xyz += v.normal * _Amount; //4 ** }** sampler2D _MainTex; void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; } ENDCG } Fallback "Diffuse" }

這是個(gè)自定義vertex的例子,效果可以實(shí)現(xiàn)點(diǎn)坐標(biāo)的放大縮小,以形成肥仔與瘦棍的效果,哈哈。第一個(gè)黑體(//1):添加一個(gè)可選參數(shù)為vertex,主要是為了給其添加一個(gè)函數(shù)vert。
第二個(gè)黑體(//2):這個(gè)_Amount對(duì)應(yīng)開(kāi)頭的那個(gè)屬性_Amount。具體是個(gè)Range值,可在shader界面外通過(guò)滑動(dòng)條改變這個(gè)值。默認(rèn)為0.5。
第三個(gè)黑體(//3):這里除了之前學(xué)過(guò)的東西外,多了個(gè)appdata_full的結(jié)構(gòu)體。這里面的結(jié)構(gòu)(載自UNITY官方論壇)如下:
struct appdata_full {

float4 vertex : POSITION;

float4 tangent : TANGENT;

float3 [normal](http://unity3d.com/support/documentation/ScriptReference/30_search.html?q=normal) : NORMAL;

float4 texcoord : TEXCOORD0;

float4 texcoord1 : TEXCOORD1;

fixed4 color : COLOR;

if defined(SHADER_API_XBOX360)

half4 texcoord2 : TEXCOORD2;

half4 texcoord3 : TEXCOORD3;

half4 texcoord4 : TEXCOORD4;

half4 texcoord5 : TEXCOORD5;

endif

};

第四個(gè)黑體(//4):就是像為個(gè)點(diǎn),換當(dāng)前法線向量的指定倍數(shù)進(jìn)行擴(kuò)展。上效果:

10.Custom data computed per-vertex

Shader "Example/Custom Vertex Data" { Properties { _MainTex ("Texture", 2D) = "white" {} } SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Lambert vertex:vert struct Input { float2 uv_MainTex; ** float3 customColor;** //1 }; void vert (inout appdata_full v, out Input o) {//2 UNITY_INITIALIZE_OUTPUT(Input,o); ** //3 o.customColor = abs(v.normal);** //4 } sampler2D _MainTex; void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; *o.Albedo = IN.customColor; //5 } ENDCG } Fallback "Diffuse" }

這個(gè)例子是用來(lái)渲染顏色的。我的分析如下:
第一處(//1):取一個(gè)顏色值,float3,對(duì)應(yīng)RGB。
第二處(//2):較前個(gè)例子,多一個(gè)Input類型的參數(shù),只為輸出使用。
第三處(//3):UNITY_INITIALIZE_OUTPUT(type,name)這個(gè)函數(shù)大有用處,主要是將叫[name]的變量請(qǐng)空改成type類型。以下是從HLSLSupport.cginc里找到的定義:

if defined(UNITY_COMPILER_HLSL)#define UNITY_INITIALIZE_OUTPUT(type,name) name = (type)0;#else#define UNITY_INITIALIZE_OUTPUT(type,name)#endif

第四處(//4):RGB顏色值當(dāng)然只能為正值,所以使用絕對(duì)值去取normal的值。
第五處(//5):在原先已經(jīng)渲染上texture顏色值的基礎(chǔ)上,加上這層自定義的顏色值。
上效果:


11.Final Color Modifier
Shader "Example/Tint Final Color" { Properties { _MainTex ( "Texture", 2D) = "white" {} _ColorTint ("Tint", Color) = (1.0, 0.6, 0.6, 1.0) } SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Lambert finalcolor:mycolor ** struct Input { float2 uv_MainTex; }; ** fixed4 _ColorTint; ** void mycolor (Input IN, SurfaceOutput o, inout fixed4 color)** { *color = _ColorTint; } sampler2D _MainTex; void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; } ENDCG } Fallback "Diffuse" }

這個(gè)例子是跟上面例子的對(duì)比,前種使用普通反射進(jìn)行疊加上顏色,此處則是直接使用finalcolor對(duì)其顏色進(jìn)行處理,這種可以處理整個(gè)模型的固定顏色值的渲染。以下做簡(jiǎn)要的分析:
1.finalcolor:mycolor :這個(gè)是另一種可選參數(shù),就是用戶自定義的顏色處理函數(shù)。函數(shù)名為mycolor.
2.mycolor函數(shù):注意到函數(shù)除了有surf的兩個(gè)參數(shù)外,還多了個(gè)顏色參數(shù),這個(gè)顏色參數(shù)就是當(dāng)前模型上顏色對(duì)象,對(duì)他的更改將直接影響全部來(lái)自于lightmap,light probe和一些相關(guān)資源的顏色值。
效果:


12.Custom Fog with Final Color Modifier
Shader "Example/Fog via Final Color" { Properties { _MainTex ("Texture", 2D) = "white" {} ** _FogColor ("Fog Color", Color) = (0.3, 0.4, 0.7, 1.0)** } SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Lambert finalcolor:mycolor vertex:myvert struct Input { float2 uv_MainTex; ** half fog;** }; void myvert (inout appdata_full v, out Input data) { ** UNITY_INITIALIZE_OUTPUT(Input,data);** ** float4 hpos = mul (UNITY_MATRIX_MVP, v.vertex);//1** ** data.fog = min (1, dot (hpos.xy, hpos.xy) * 0.1); //2** } fixed4 _FogColor; void mycolor (Input IN, SurfaceOutput o, inout fixed4 color) { ** fixed3 fogColor = _FogColor.rgb;** ** #ifdef UNITY_PASS_FORWARDADD //3** ** fogColor = 0;** ** //3 #endif** ** //3 color.rgb = lerp (color.rgb, fogColor, IN.fog); //4 ** } sampler2D _MainTex; void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; } ENDCG } Fallback "Diffuse" }

這個(gè)很高難度,里面還有些之前沒(méi)用到過(guò)的函數(shù),以下是我的理解:
第一處(//1):mul是矩陣相乘的函數(shù)。UNITY_MATRIX_MVP是model、view、projection三個(gè)矩陣相乘出來(lái)的4x4的矩陣。v.vertex是一個(gè)float4的變量,可理解成4x1的矩陣,兩者相乘,則得出一個(gè)float4,這個(gè)值就是視角窗口的坐標(biāo)值,這個(gè)坐標(biāo)就跟camera的關(guān)聯(lián)了。
第二處(//2):這個(gè)fog的浮點(diǎn)值就是其強(qiáng)度,范圍一般在-1到1之間,說(shuō)一般,只是我個(gè)人建議的值,設(shè)成其他也行,只是沒(méi)多大意義。越負(fù)就越黑。再看后面這個(gè)點(diǎn)積,這個(gè)仔細(xì)一想,不難理解,其實(shí)就是為了達(dá)到一種擴(kuò)散的效果,因此兩個(gè)一樣的向量相乘,其實(shí)就是直接對(duì)坐標(biāo)做平方擴(kuò)展,這樣fog就更有霧的感覺(jué)。

第三處(//3):這個(gè)宏不好找,就看官方對(duì)這個(gè)例子的解釋為正向渲染時(shí)的額外通道。字面不好理解,多多嘗試過(guò)可以有所發(fā)現(xiàn),其實(shí)就是在霧氣漸漸消失處那塊額外的渲染區(qū)。可以將fogColor = 0; 改成fogColor = fixed3(1,0,0)。外面霧氣顏色再選成白色,效果則如下:
霧氣改成綠色后:效果如下:

第四處(//4):lerp函數(shù)是個(gè)有趣的函數(shù)。第一個(gè)參數(shù)是左邊界,第二個(gè)參數(shù)是右邊界,第三個(gè)相當(dāng)于一個(gè)值介于0到1之間的游標(biāo)。游標(biāo)為0,則為左邊界,為1為右邊界,取中間值則是以此類推,取插值。其實(shí)也可以把它看成百分比。這里的fog則可以看來(lái)那個(gè)游標(biāo),值越大,則越接近fogColor,越小越接近原色。
原shader所出來(lái)的效果再來(lái)張:

13.Linear Fog

Shader "Example/Linear Fog"{ Properties { _MainTex ("Base (RGB)", 2D) = "white" {} } SubShader { Tags { "RenderType"="Opaque" } LOD 200 //1 CGPROGRAM #pragma surface surf Lambert finalcolor:mycolor vertex:myvert sampler2D _MainTex; uniform half4 unity_FogColor; //2 uniform half4 unity_FogStart; uniform half4 unity_FogEnd; struct Input { float2 uv_MainTex; half fog; }; void myvert (inout appdata_full v, out Input data) { UNITY_INITIALIZE_OUTPUT(Input,data); float pos = length(mul (UNITY_MATRIX_MV, v.vertex).xyz); //3 float diff = unity_FogEnd.x - unity_FogStart.x; //4 float invDiff = 1.0f / diff; //5 data.fog = clamp ((unity_FogEnd.x - pos) * invDiff, 0.0, 1.0); //6 } void mycolor (Input IN, SurfaceOutput o, inout fixed4 color) { fixed3 fogColor = unity_FogColor.rgb; #ifdef UNITY_PASS_FORWARDADD fogColor = 0; #endif color.rgb = lerp (fogColor, color.rgb, IN.fog); } 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"}

這個(gè)官方只貼出了代碼,無(wú)任何解釋。網(wǎng)上也未曾看到有人解答,在此為大家分析下。其實(shí)這個(gè)與上面那個(gè)例子相比之下,采用的fog的源頭變了,這里是獲取rendersettings里的fog來(lái)設(shè)置Fog的顏色、強(qiáng)度與起點(diǎn)終點(diǎn)等。以下進(jìn)行解析:
第一處(//1):LOD 200,200是個(gè)代號(hào),設(shè)成此的目的就是限制shader級(jí)別只到200為止,高過(guò)200的不采用,即使顯卡支持,也不會(huì)使用高過(guò)200的shader級(jí)別的渲染方式。官方的解釋:http://docs.unity3d.com/Documentation/Components/SL-ShaderLOD.html
第二處(//2):此處標(biāo)記uniform的意圖就是讓Cg可以使用此變量。因此這三個(gè)uniform變量均來(lái)自于RenderSetting中。你可以預(yù)先設(shè)置好三個(gè)值。
第三處(//3):length函數(shù)用于取一個(gè)向量的長(zhǎng)度,如果是float3則采取如下形式:

float length(float3 v){ return sqrt(dot(v,v));}

就是點(diǎn)積取平方根。第四處(//4):計(jì)算fog起終點(diǎn)間的反差。
第五處(//5):將4中算得的diff置反過(guò)來(lái)。
第六處(//6):則將算出來(lái)的離視角的距離與0到1之間進(jìn)行比對(duì),小于0則為0,大于1則為1,范圍之內(nèi)就是其原值,總的來(lái)說(shuō),利用clamp函數(shù)防止其出界。
分析下原理:咱們先將rendersetting里的顏色設(shè)成紅色,fog start 設(shè)成0, fog end設(shè)成50。這時(shí)算出的diff = 50, invdiff = 1/50。將原fog的計(jì)算稍做簡(jiǎn)化,得出如下結(jié)果:
fog = clamp((1 - pos/50) , 0 , 1);這個(gè)式子很是明了,pos是距離,即距離越遠(yuǎn),clamp里值越小,根據(jù)后面這句:
color.rgb = lerp (fogColor, color.rgb, IN.fog);

我們就可以判斷出其越靠近fogColor,霧氣就會(huì)越重。
最后上個(gè)效果圖:這里選的是Linear的fog。


到此,所有的surface shader的官方例子都詳細(xì)的介紹完了。哎,開(kāi)源中國(guó)對(duì)cg無(wú)什么代碼顯示支持,大家要代碼看不清,可以直接去官網(wǎng)上去面看。http://docs.unity3d.com/Documentation/Components/SL-SurfaceShaderExamples.html
這里送上本文的項(xiàng)目工程:http://pan.baidu.com/s/1xindN
四、學(xué)習(xí)技巧
這里是我個(gè)人的一些觀點(diǎn):
1.遇問(wèn)題先找官網(wǎng),找官網(wǎng)論壇,找官網(wǎng)文檔。
2.學(xué)會(huì)從軟件根目錄下的CGIncludes文件夾下找相關(guān)的函數(shù)宏定義。
3.積累相關(guān)線性代數(shù)與計(jì)算機(jī)圖形學(xué)的知識(shí),學(xué)習(xí)會(huì)更輕松些。

文中要有不對(duì)的地方,歡迎大家留言指正,其實(shí)與愛(ài)好Unity的人互相學(xué)習(xí)討論!
下文將為大家送上對(duì)于官網(wǎng)的surface shader的其他方面的學(xué)習(xí)手記。敬請(qǐng)期待。。積極更新中。。

本文系原創(chuàng),全部解析內(nèi)容來(lái)自自我學(xué)習(xí)的記錄,請(qǐng)?jiān)谵D(zhuǎn)載時(shí)注明出處,謝謝#

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 摘要: 這篇文章主要介紹Surface Shaders基礎(chǔ)及Examples詳盡解析 What?? Shader,...
    男爵卡波閱讀 2,546評(píng)論 0 52
  • 動(dòng)機(jī)自己使用Unity3D也有一段時(shí)間了,但是很多時(shí)候是流于表面,更多地是把這個(gè)引擎簡(jiǎn)單地用作腳本控制,而對(duì)更深入...
    JumboWu閱讀 2,214評(píng)論 0 24
  • <轉(zhuǎn)>我也忘了轉(zhuǎn)自哪里,抱歉,感謝原作者 什么是Shader Shader(著色器)是一段能夠針對(duì)3D對(duì)象進(jìn)行操作...
    星易乾川閱讀 5,842評(píng)論 1 16
  • 文 ▏快手刀 01 我所在的小區(qū)是這個(gè)城市里最普通的小區(qū)之一。和你想的一樣,十幾棟30多層的高樓林立在一塊毫無(wú)特色...
    快手刀閱讀 469評(píng)論 0 0
  • 我以前對(duì)貓是有成見(jiàn)的,覺(jué)得孤傲,不忠誠(chéng),特立獨(dú)行,種種比不上狗的地方。 有一天我去書店看書,看到一只躺在高腳餐椅上...
    禿子lee閱讀 350評(píng)論 0 0

友情鏈接更多精彩內(nèi)容