摘要: 這篇文章主要介紹Surface Shaders基礎(chǔ)及Examples詳盡解析
What?? Shader,看起來好高級的樣子筷笨,是的憔鬼,這是Unity中高級進(jìn)階的必備。因此胃夏,兄弟我就在此記下我學(xué)習(xí)官網(wǎng)的一些心得轴或。
此為一。主要介紹些Surface Shaders的知識仰禀。具體的大家也可去官網(wǎng)(如下)學(xué)習(xí)照雁。
http://docs.unity3d.com/Documentation/Components/SL-SurfaceShaders.html
一、概念篇
1.基準(zhǔn):unity里的shader并不是一門獨(dú)特的語言答恶,而是一種代碼生成方式饺蚊,且可將低層次且復(fù)雜的shader編程進(jìn)行簡化。但同時(shí)你也還是得使用Cg/HLSL來寫的悬嗓。
2.原理:寫一個(gè)函數(shù)卸勺,以UVs或者一些數(shù)據(jù)為入口,然后以SurfaceOutput為輸出烫扼。同時(shí)在SurfaceOutput這個(gè)結(jié)構(gòu)體里還有不同的屬性曙求。這樣對于這個(gè)函數(shù)來說,他的執(zhí)行過程會(huì)生成vertex和pixel的Shader映企,并且傳遞一些渲染的路徑悟狱。
3.結(jié)構(gòu):輸出結(jié)構(gòu):
structSurfaceOutput{? ? 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ù)后膏萧,以后來的shader為主。
3.指令詳細(xì):
#pragmasurface surfaceFunction lightModel[optionalparams]
=>surfaceFunction,沒什么好說榛泛,肯定是函數(shù)名了蝌蹂。
=>lightModel是所采用的光照模型〔芟牵可以自己寫也可使用內(nèi)置如Lambert和BlinnPhong.
=>optionalparams:可選參數(shù)孤个,一堆可選包括透明度,頂點(diǎn)與顏色函數(shù)沛简,投射貼花shader等等齐鲤。具體用到可以細(xì)選。
另外這里有一個(gè)功能覆享。在Surface shader的CGPROGRAM里添加 #pragma debug [內(nèi)容]佳遂。可在編譯結(jié)果的文件中看到丑罪。寫多少都行。但嘗試在其他種shader下不行。
三、實(shí)例學(xué)習(xí):
1.Simple:
Shader"Example/Diffuse Simple"{SubShader{Tags{"RenderType"="Opaque"}CGPROGRAM#pragmasurface surfLambertstructInput{float4 color:COLOR;};voidsurf(InputIN,inoutSurfaceOutputo){o.Albedo=1;}ENDCG}Fallback"Diffuse"}
第一個(gè)淌哟。行行來:
第一行:寫個(gè)名字徒仓。這也有講究的腐碱。斜線左邊為其父類的組,無則新增掉弛,有則累加症见,右邊才是真正的名字。注意殃饿,這些shader名不像C#腳本谋作,無需文件名與shader名相同。
第二壁晒、三行:接下來就在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)識,意思是可為輸入?yún)?shù)也可為輸出參數(shù)兔辅。Albedo根據(jù)前面介紹到的腊敲,是一個(gè)rgb的值伤锚,如果給一個(gè)1清蚀,其實(shí)就是float3(1,1,1),就是反射出來的顏色為白色千所,如果為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 LambertstructInput {float2 uv_MainTex;};sampler2D _MainTex;voidsurf (Input IN,inoutSurfaceOutput 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開頭指代后方材質(zhì)的uv值始苇,因此uv不變砌烁,后面的可以根據(jù)開頭起的名字動(dòng)態(tài)換。還有哦催式,這種類似于_MainTex的命名方式是CG推薦的函喉,其實(shí)不用下劃線也OK的。
第三個(gè)黑體:這個(gè)Sampler2D荣月,可以理解為引用一個(gè)2D?Texture管呵。因?yàn)橄旅娴腡ex2D函數(shù)需要這種類型。所以說這個(gè)后面的名字要與Properties里的對應(yīng)一樣才行哺窄。
第四個(gè)黑體:Tex2D捐下,這玩意就是根據(jù)對應(yīng)材質(zhì)上所有的點(diǎn)找指定 2DSample上的Texture信息,此處需要其RGB信息堂氯,就打出來賦給了其反射值蔑担。所以對有材質(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 LambertstructInput {? ? ? ? float2 uv_MainTex;? ? ? ? float2 uv_BumpMap;? ? ? };? ? ? sampler2D _MainTex;sampler2D _BumpMap;voidsurf (Input IN,inoutSurfaceOutput 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è)采集器蹲蒲。采集下來上面的材質(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ì)針對移動(dòng)平臺或OPENGL ES平臺采用 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#pragmasurface surf BlinnPhongstructInput? {? float2 uv_MainTex;? };//法線范圍轉(zhuǎn)換:單位法線 float3(x,y,z),x瞬逊,y,z的取值范圍是 [-1,1]显歧。在法線貼圖中被壓縮在顏色的范圍[0,1]中,所以需要轉(zhuǎn)換//(1)RGB法線貼圖float3expand(float3 v){return(v -0.5) *2; }//(2)DXT5nm法線貼圖float3expand2(float4 v){fixed3 normal;normal.xy = v.wy *2-1;normal.z = sqrt(1- normal.x*normal.x - normal.y * normal.y);returnnormal;}? sampler2D _MainTex;? sampler2D _NormalMap;voidsurf(Input IN,inout SurfaceOutput o){? half4 c = tex2D(_MainTex, IN.uv_MainTex);? o.Albedo = c.rgb;? o.Alpha = c.a;//對法線貼圖進(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的對應(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 Lambertstruct Input {float2 uv_MainTex;float2 uv_BumpMap;float3 viewDir; //3};? ? ? sampler2D _MainTex;? ? ? sampler2D _BumpMap;float4 _RimColor;//4float_RimPower;//5void 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));//6o.Emission = _RimColor.rgb * pow (rim, _RimPower);//7}? ? ? ENDCG? ? }? ? Fallback"Diffuse"}
新增的一些東西士骤,我都用數(shù)字標(biāo)注了。以下進(jìn)行詳細(xì)解讀:
第一處(//1):上一個(gè)Color類型的顯示為Rim Color的變量骚腥。顏色值RGBA對應(yīng)0.26,0.19,0.16,0.0
第二處(//2):這個(gè)Range類型的變量敦间,結(jié)果還是一個(gè)float瓶逃。只是這個(gè)float是在這個(gè)range之內(nèi)束铭。為什么這么定義呢。如果超多厢绝,或過小契沫,則使用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è)變量對應(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í)取反來。這樣泳挥,夾角越大然痊,所反射上的顏色就越多。于是就得到的兩邊發(fā)光的效果屉符。哈哈這樣明了吧剧浸。
這里介紹一下這個(gè)half锹引。CG里還有類似的float和fixed。half是一種低精度的float唆香,但有時(shí)也會(huì)被選擇成與float一樣的精度嫌变。fragment是一定會(huì)支持fixed類型,同時(shí)也會(huì)有可能將其精度設(shè)成與float一樣躬它,這個(gè)比較復(fù)雜腾啥,后面篇章學(xué)到fragment時(shí)再深入探討。
以下為與3的對比冯吓,大家一下就知道誰是用了rim color的吧倘待。對!下面那個(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 LambertstructInput {? ? ? ? ? float2 uv_MainTex;? ? ? ? ? float2 uv_BumpMap;float2 uv_Detail;};? ? ? sampler2D _MainTex;? ? ? sampler2D _BumpMap;sampler2D _Detail;voidsurf (Input IN,inoutSurfaceOutput 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 LambertstructInput {? ? ? ? ? float2 uv_MainTex;float4 screenPos;};? ? ? sampler2D _MainTex;? ? ? sampler2D _Detail;voidsurf (Input IN,inoutSurfaceOutput o) {? ? ? ? ? o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;float2 screenUV = IN.screenPos.xy / IN.screenPos.w;//2screenUV *= 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)到的位置而變換圖片摄咆。
這里只需要說三點(diǎn):
1.關(guān)于screenPos:screenPos是一個(gè)三維點(diǎn),但是用齊次坐標(biāo)的形式表示出來就是(x,y,z,w)人断,根據(jù)齊次坐標(biāo)的性質(zhì)吭从。(x,y,z,w)的齊次坐標(biāo)對應(yīng)三維點(diǎn)(x/w,y/w,z/w)。因此把w值除掉可以看來是一種Normalize的作法恶迈,這樣就取出了實(shí)際的屏幕xy的UV值涩金。
2.對screenUV進(jìn)行倍剩:此處剩float2(8,6)意為將原獲取到屏幕尺寸進(jìn)行拉大的倍數(shù)暇仲。即x軸拉大8倍步做,y軸拉大6倍。
3.如何就平鋪了剛好一行8個(gè)奈附,一列6個(gè)了呢全度? 原因我覺得是在于2d Texture自己是按Normalize后進(jìn)行鋪的,因此在//2(剛轉(zhuǎn)完標(biāo)準(zhǔn)的)screenPos后斥滤,將其剩多少即便將原圖鋪多少張将鸵。
OK勉盅。明了。其實(shí)這個(gè)東西可以拿來做放大鏡的應(yīng)用顶掉。上圖:
7. Cubemap reflection
Shader"Example/WorldRefl"{? ? Properties {? ? ? _MainTex ("Texture",2D) ="white"{}_Cube ("Cubemap", CUBE) =""{}}? ? SubShader {? ? ? Tags {"RenderType"="Opaque"}CGPROGRAM#pragma surface surf LambertstructInput {? ? ? ? ???float2 uv_MainTex;float3 worldRefl;};? ? ? sampler2D _MainTex;samplerCUBE _Cube;voidsurf (Input IN,inoutSurfaceOutput 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 LambertstructInput {? ? ? ? ? float2 uv_MainTex;? ? ? ? ? float2 uv_BumpMap;? ? ? ? ? float3 worldRefl;INTERNAL_DATA};? ? ? sampler2D _MainTex;? ? ? sampler2D _BumpMap;? ? ? samplerCUBE _Cube;voidsurf (Input IN,inoutSurfaceOutput 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è)角度看過去以顯示不同的渲染效果簿透。
以下說明:
1. worldRefl:即為世界空間的反射向量移袍。
2. texCUBE:將反射向量一個(gè)個(gè)的往_Cube反射盒上找出然后做為Emission反射出來。
3. 第二個(gè)例子只是將其用在Normal反射后萎战,這樣一定要多添加一個(gè)INTERNAL_DATA的屬性咐容,另外也需用到WorldReflectionVectore方法取其利用Normal后的反射向量值。
類似于的效果蚂维,可見官網(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 OffCGPROGRAM#pragma surface surf LambertstructInput {? ? ? ? ? float2 uv_MainTex;? ? ? ? ? float2 uv_BumpMap;float3 worldPos;};? ? ? sampler2D _MainTex;? ? ? sampler2D _BumpMap;voidsurf (Input IN,inoutSurfaceOutput o) {clip (frac((IN.worldPos.y+IN.worldPos.z*0.1) *5) -0.5);o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;? ? ? ? ? o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));? ? ? }? ? ? ENDCG? ? }? ? Fallback"Diffuse"}
在看完這段后,我自己另外又加一段奄妨,以作對比:
float3_tWorldPos;voidsurf(Input IN, inout SurfaceOutput o){_tWorldPos = IN.screenPos.xyz / IN.screenPos.w;//clip (frac((IN.worldPos.y+IN.worldPos.z*0.1) * 5) - 0.5);clip (frac((_tWorldPos.y+_tWorldPos.z*0.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 取出來是 0.23。clip函數(shù)用于清Pixel的砸抛,負(fù)值情況下才進(jìn)行清pixel评雌。且越小,即絕對值越大則清越多直焙。這里注意那個(gè)* 5景东,仔細(xì)一想,如果frac出來的值越大奔誓,-0.5值就越大斤吐,絕對值就越小,因此這樣清掉的pixel越少厨喂,所以就可以間接的增加分段的次數(shù)和措。那為什么要+IN.worldPos.z*0.1呢,主要原因就是空開的斷添加一個(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 Lambertvertex:vert//1structInput {? ? ? ? ? float2 uv_MainTex;? ? ? };float_Amount;//2void vert (inout appdata_full v) { //3v.vertex.xyz += v.normal * _Amount; //4}sampler2D _MainTex;voidsurf (Input IN,inoutSurfaceOutput 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對應(yīng)開頭的那個(gè)屬性_Amount寨辩。具體是個(gè)Range值,可在shader界面外通過滑動(dòng)條改變這個(gè)值歼冰。默認(rèn)為0.5靡狞。
第三個(gè)黑體(//3):這里除了之前學(xué)過的東西外,多了個(gè)appdata_full的結(jié)構(gòu)體隔嫡。這里面的結(jié)構(gòu)(載自UNITY官方論壇)如下:
structappdata_full{
float4 vertex : POSITION;
float4 tangent : TANGENT;
float3normal: 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 Lambertvertex:vertstruct Input {float2 uv_MainTex;float3 customColor;//1};voidvert (inoutappdata_full v,outInput o) {//2UNITY_INITIALIZE_OUTPUT(Input,o);//3? ???????? ?o.customColor = abs(v.normal);//4}? ? ? sampler2D _MainTex;voidsurf (Input IN,inoutSurfaceOutput o) {? ? ? ? ? o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;o.Albedo *= IN.customColor;//5}? ? ? ENDCG? ? }? ? Fallback"Diffuse"}
這個(gè)例子是用來渲染顏色的腮恩。我的分析如下:
第一處(//1):取一個(gè)顏色值梢杭,float3,對應(yīng)RGB秸滴。
第二處(//2):較前個(gè)例子武契,多一個(gè)Input類型的參數(shù),只為輸出使用荡含。
第三處(//3):UNITY_INITIALIZE_OUTPUT(type,name)這個(gè)函數(shù)大有用處咒唆,主要是將叫[name]的變量請空改成type類型。以下是從HLSLSupport.cginc里找到的定義:
#ifdefined(UNITY_COMPILER_HLSL)#defineUNITY_INITIALIZE_OUTPUT(type,name) name = (type)0;#else#defineUNITY_INITIALIZE_OUTPUT(type,name)#endif
第四處(//4):RGB顏色值當(dāng)然只能為正值释液,所以使用絕對值去取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 Lambertfinalcolor:mycolorstruct Input {float2 uv_MainTex;? ? ? };fixed4 _ColorTint;voidmycolor (Input IN, SurfaceOutput o,inoutfixed4 color){color *= _ColorTint;}? ? ? sampler2D _MainTex;voidsurf (Input IN,inoutSurfaceOutput o) {? ? ? ? ? o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;? ? ? }? ? ? ENDCG? ? }? ? Fallback"Diffuse"}
這個(gè)例子是跟上面例子的對比恨溜,前種使用普通反射進(jìn)行疊加上顏色,此處則是直接使用finalcolor對其顏色進(jìn)行處理找前,這種可以處理整個(gè)模型的固定顏色值的渲染糟袁。以下做簡要的分析:
1.finalcolor:mycolor :這個(gè)是另一種可選參數(shù),就是用戶自定義的顏色處理函數(shù)躺盛。函數(shù)名為mycolor.
2.mycolor函數(shù):注意到函數(shù)除了有surf的兩個(gè)參數(shù)外项戴,還多了個(gè)顏色參數(shù),這個(gè)顏色參數(shù)就是當(dāng)前模型上顏色對象槽惫,對他的更改將直接影響全部來自于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#pragmasurface surf Lambertfinalcolor:mycolor vertex:myvertstructInput {? ? ? ? ? float2 uv_MainTex;half fog;};voidmyvert(inout appdata_full v,outInput data){UNITY_INITIALIZE_OUTPUT(Input,data);float4 hpos = mul (UNITY_MATRIX_MVP, v.vertex);//1data.fog = min (1, dot (hpos.xy, hpos.xy) * 0.1); //2}? ? ? fixed4 _FogColor;voidmycolor(Input IN, SurfaceOutput o, inout fixed4 color){fixed3 fogColor = _FogColor.rgb;#ifdef UNITY_PASS_FORWARDADD //3fogColor = 0;//3?????? ????????????#endif//3 ????????????color.rgb = lerp (color.rgb, fogColor, IN.fog); //4}sampler2D _MainTex;voidsurf(Input IN, inout SurfaceOutput o){? ? ? ? ? o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;? ? ? }? ? ? ENDCG? ? }? ? Fallback"Diffuse"}
這個(gè)很高難度辩撑,里面還有些之前沒用到過的函數(shù),以下是我的理解:
第一處(//1):mul是矩陣相乘的函數(shù)仿耽。UNITY_MATRIX_MVP是model合冀、view、projection三個(gè)矩陣相乘出來的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之間,說一般完残,只是我個(gè)人建議的值伏钠,設(shè)成其他也行,只是沒多大意義坏怪。越負(fù)就越黑贝润。再看后面這個(gè)點(diǎn)積,這個(gè)仔細(xì)一想铝宵,不難理解,其實(shí)就是為了達(dá)到一種擴(kuò)散的效果华畏,因此兩個(gè)一樣的向量相乘鹏秋,其實(shí)就是直接對坐標(biāo)做平方擴(kuò)展,這樣fog就更有霧的感覺亡笑。
第三處(//3):這個(gè)宏不好找侣夷,就看官方對這個(gè)例子的解釋為正向渲染時(shí)的額外通道。字面不好理解仑乌,多多嘗試過可以有所發(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則可以看來那個(gè)游標(biāo)帝火,值越大溜徙,則越接近fogColor,越小越接近原色购公。
原shader所出來的效果再來張:
13.Linear Fog
Shader"Example/Linear Fog"{? ??Properties {? ? _MainTex ("Base (RGB)",2D) ="white"{}? }? SubShader {? ? Tags {"RenderType"="Opaque"}? ? LOD200//1CGPROGRAM#pragmasurface surf Lambert finalcolor:mycolor vertex:myvertsampler2D _MainTex;? ? uniform half4 unity_FogColor;//2uniform half4 unity_FogStart;? ? uniform half4 unity_FogEnd;structInput {? ? ? float2 uv_MainTex;? ? ? half fog;? ? };voidmyvert(inout appdata_full v,outInput data){? ? ? UNITY_INITIALIZE_OUTPUT(Input,data);floatpos = length(mul (UNITY_MATRIX_MV, v.vertex).xyz);//3floatdiff = unity_FogEnd.x - unity_FogStart.x;//4floatinvDiff =1.0f / diff;//5data.fog = clamp ((unity_FogEnd.x - pos) * invDiff,0.0,1.0);//6}voidmycolor(Input IN, SurfaceOutput o, inout fixed4 color){? ? ? fixed3 fogColor = unity_FogColor.rgb;#ifdef UNITY_PASS_FORWARDADDfogColor =0;#endifcolor.rgb = lerp (fogColor, color.rgb, IN.fog);? ? }voidsurf(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ǎng)上也未曾看到有人解答宏浩,在此為大家分析下知残。其實(shí)這個(gè)與上面那個(gè)例子相比之下,采用的fog的源頭變了比庄,這里是獲取rendersettings里的fog來設(shè)置Fog的顏色求妹、強(qiáng)度與起點(diǎn)終點(diǎn)等。以下進(jìn)行解析:
第一處(//1):LOD 200佳窑,200是個(gè)代號制恍,設(shè)成此的目的就是限制shader級別只到200為止,高過200的不采用神凑,即使顯卡支持净神,也不會(huì)使用高過200的shader級別的渲染方式。官方的解釋:http://docs.unity3d.com/Documentation/Components/SL-ShaderLOD.html
第二處(//2):此處標(biāo)記uniform的意圖就是讓Cg可以使用此變量溉委。因此這三個(gè)uniform變量均來自于RenderSetting中鹃唯。你可以預(yù)先設(shè)置好三個(gè)值。
第三處(//3):length函數(shù)用于取一個(gè)向量的長度瓣喊,如果是float3則采取如下形式:
floatlength(float3 v){returnsqrt(dot(v,v));}
就是點(diǎn)積取平方根坡慌。
第四處(//4):計(jì)算fog起終點(diǎn)間的反差。
第五處(//5):將4中算得的diff置反過來藻三。
第六處(//6):則將算出來的離視角的距離與0到1之間進(jìn)行比對洪橘,小于0則為0,大于1則為1棵帽,范圍之內(nèi)就是其原值熄求,總的來說,利用clamp函數(shù)防止其出界岖寞。
分析下原理:咱們先將rendersetting里的顏色設(shè)成紅色抡四,fog start 設(shè)成0, fog end設(shè)成50。
這時(shí)算出的diff = 50, invdiff = 1/50指巡。將原fog的計(jì)算稍做簡化淑履,得出如下結(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ì)的介紹完了至壤。哎,開源中國對cg無什么代碼顯示支持枢纠,大家要代碼看不清像街,可以直接去官網(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ǎng)镰绎,找官網(wǎng)論壇,找官網(wǎng)文檔木西。
2.學(xué)會(huì)從軟件根目錄下的CGIncludes文件夾下找相關(guān)的函數(shù)宏定義畴栖。
3.積累相關(guān)線性代數(shù)與計(jì)算機(jī)圖形學(xué)的知識,學(xué)習(xí)會(huì)更輕松些八千。
文中要有不對的地方吗讶,歡迎大家留言指正,其實(shí)與愛好Unity的人互相學(xué)習(xí)討論恋捆!
下文將為大家送上對于官網(wǎng)的surface shader的其他方面的學(xué)習(xí)手記关翎。敬請期待。鸠信。積極更新中。论寨。
#本文系原創(chuàng)星立,全部解析內(nèi)容來自自我學(xué)習(xí)的記錄,請?jiān)谵D(zhuǎn)載時(shí)注明出處葬凳,謝謝#