1. 程式人生 > >Unity shader 官網文件全方位學習(一)

Unity shader 官網文件全方位學習(一)

What?? Shader,看起來好高階的樣子,是的,這是Unity中高階進階的必備。因此,兄弟我就在此記下我學習官網的一些心得。

此為一。主要介紹些Surface Shaders的知識。具體的大家也可去官網(如下)學習。

一、概念篇

1.基準:unity裡的shader並不是一門獨特的語言,而是一種程式碼生成方式,且可將低層次且複雜的shader程式設計進行簡化。但同時你也還是得使用Cg/HLSL來寫的。

2.原理:寫一個函式,以UVs或者一些資料為入口,然後以SurfaceOutput為輸出。同時在SurfaceOutput這個結構體裡還有不同的屬性。這樣對於這個函式來說,他的執行過程會生成vertex和pixel的Shader,並且傳遞一些渲染的路徑。

3.結構:輸出結構:

  1. struct SurfaceOutput {

  2. half3 Albedo;

  3. half3 Normal;

  4. half3 Emission;

  5. half Specular;

  6. half Gloss;

  7. half Alpha;

  8. };


Albedo,是漫反射的顏色值。
Normal,法線座標
Emission,自發光顏色
Specular,鏡面反射係數
Gloss,光澤係數
Alpha,透明度係數

二、程式設計規則

1.要寫在CGPROGRAM..ENDCG的SubShader的塊裡。不可寫在Pass裡。

2.shader的名字是可以重複的,重複後,以後來的shader為主。

3.指令詳細:

  1. #pragma surface surfaceFunction lightModel [optionalparams]

=>surfaceFunction,沒什麼好說,肯定是函式名了。

=>lightModel是所採用的光照模型。可以自己寫也可使用內建如Lambert和BlinnPhong.

=>optionalparams:可選引數,一堆可選包括透明度,頂點與顏色函式,投射貼花shader等等。具體用到可以細選。

另外這裡有一個功能。在Surface shader的CGPROGRAM裡新增 #pragma debug [內容]。可在編譯結果的檔案中看到。寫多少都行。但嘗試在其他種shader下不行。

三、例項學習:

1.Simple: 

  1. Shader "Example/Diffuse Simple" {

  2. SubShader {

  3. Tags { "RenderType" = "Opaque" }

  4. CGPROGRAM

  5. #pragma surface surf Lambert

  6. struct Input { float4 color : COLOR; };

  7. void surf (Input IN, inout SurfaceOutput o) {

  8. o.Albedo = 1;

  9. }

  10. ENDCG

  11. }

  12. Fallback "Diffuse" }


第一個。行行來:

第一行:寫個名字。這也有講究的。斜線左邊為其父類的組,無則新增,有則累加,右邊才是真正的名字。注意,這些shader名不像C#指令碼,無需檔名與shader名相同。

第二、三行:接下來就在SubShader裡新增內容,SubShader是可以有多個的。然後上一個Tags,此處只用到RenderType這種,另外的還有Rendering order, ForceNoShadowCasting..等。這些本階段暫不研究。

第四行:上一條指令,裡面指定響應方法為surf且採用Lambert的光照模型。這個必須有的。

第五行:這個結構體,記得名字不能改,只能為Input。裡面一個四元素的顏色值(RGBA)。

第七到第九行:第一個引數,純輸入的上述結構體引數。第二個引數,inout標識,意思是可為輸入引數也可為輸出引數。Albedo根據前面介紹到的,是一個rgb的值,如果給一個1,其實就是float3(1,1,1),就是反射出來的顏色為白色,如果為100,則是加強反射強度,並不會改變其顏色。為0或為負數時道理類似。

最後Fallback,後方的是自帶的shader,可以用自己自定義好的。這裡這句的意思是,如果所有subshader在當前顯示卡都不支援,則預設返回自帶的Diffuse。

2.Texture:

  1. Shader "Example/Diffuse Texture"

  2. { Properties { _MainTex ("Texture", 2D) = "white" {} } SubShader {

  3. Tags { "RenderType" = "Opaque" }

  4. CGPROGRAM

  5. #pragma surface surf Lambert

  6. struct Input { float2 uv_MainTex; }; sampler2D _MainTex;

  7. void surf (Input IN, inout SurfaceOutput o) {

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

  9. }

  10. ENDCG

  11. }

  12. Fallback "Diffuse"

  13. }

這個例子呢。其實只是第一個的基礎上添加了一個2D屬性顯示名為Texture。以下解析:

第一個黑體:新增一個名叫_MainTex的屬性,指定其為2D型別且顯示為Texture。"white"那塊可不是亂寫的,是unity的build-in的一些textures的名稱,而不是單純顏色名字。意思是當預設時顯示為名叫white的材質。如改成red(即使用名叫red的材質,如果有其他也可叫其名字),則效果如下:

第二個黑體:uv_MainTex。這其中大有玄機,uv開頭指代後方材質的uv值,因此uv不變,後面的可以根據開頭起的名字動態換。還有哦,這種類似於_MainTex的命名方式是CG推薦的,其實不用下劃線也OK的。

第三個黑體:這個Sampler2D,可以理解為引用一個2D Texture。因為下面的Tex2D函式需要這種型別。所以說這個後面的名字要與Properties裡的對應一樣才行。

第四個黑體:Tex2D,這玩意就是根據對應材質上所有的點找指定 2DSample上的Texture資訊,此處需要其RGB資訊,就打出來賦給了其反射值。所以對有材質圖的情況下,要顯示出圖,還是要相應的反射其原圖的rgb值。

3.Normal mapping

  1. Shader "Example/Diffuse Bump" {

  2. Properties {

  3. _MainTex (

  4. "Texture", 2D) = "white" {}

  5. _BumpMap ("Bumpmap", 2D) = "bump" {}

  6. }

  7. SubShader {

  8. Tags { "RenderType" = "Opaque" }

  9. CGPROGRAM

  10. #pragma surface surf Lambert

  11. struct Input {

  12. float2 uv_MainTex;

  13. float2 uv_BumpMap;

  14. };

  15. sampler2D _MainTex;

  16. sampler2D _BumpMap;

  17. void surf (Input IN, inout SurfaceOutput o) {

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

  19. o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));

  20. }

  21. ENDCG

  22. }

  23. Fallback "Diffuse"

  24. }


這個例子里加了個凹凸貼圖,可實現類似一些很漂亮的凹凸效果。

第一個黑體:加一個2D型別的材質,預設為bump。(即帶有凹凸效果的)。

第二個黑體:上一個採集器。採集下來上面的材質。

第三個黑體:有講究,這個UnpackNormal是unity自帶的標準解壓法線用的,所謂解壓,我暫時學習到的只是將法線的區間進行變換。由於tex2D(_BumpMap, IN.uv_BumpMap)取出的是帶壓縮的[0,1]之間,需要轉成[-1,1]。這個函式會針對移動平臺或OPENGL ES平臺採用 RGB法線貼圖,其他採用DXT5nm貼圖。為此也可自己寫。也在網上找到了一些資料,如下參考:

  1. // Shader: 帶法線貼圖的Surface Shader

  2. // Author: 風宇衝

  3. Shader "Custom/3_NormalMap" {

  4. Properties

  5. {

  6. _MainTex ("Texture", 2D) = "white" {}

  7. _NormalMap ("NormalMap", 2D) = "white" {}

  8. }

  9. Subshader

  10. {

  11. CGPROGRAM

  12. #pragma surface surf BlinnPhong

  13. struct Input

  14. {

  15. float2 uv_MainTex;

  16. };

  17. //法線範圍轉換:單位法線 float3(x,y,z),x,y,z的取值範圍是 [-1,1]。在法線貼圖中被壓縮在顏色的範圍[0,1]中,所以需要轉換

  18. //(1)RGB法線貼圖

  19. float3 expand(float3 v) { return (v - 0.5) * 2; }

  20. //(2)DXT5nm法線貼圖

  21. float3 expand2(float4 v)

  22. {

  23. fixed3 normal;

  24. normal.xy = v.wy * 2 - 1;

  25. normal.z = sqrt(1 - normal.x*normal.x - normal.y * normal.y);

  26. return normal;

  27. }

  28. sampler2D _MainTex;

  29. sampler2D _NormalMap;

  30. void surf(Input IN,inout SurfaceOutput o)

  31. {

  32. half4 c = tex2D(_MainTex, IN.uv_MainTex);

  33. o.Albedo = c.rgb;

  34. o.Alpha = c.a;

  35. //對法線貼圖進行取樣,取得壓縮在顏色空間裡的法線([0,1])

  36. float4 packedNormal = tex2D(_NormalMap, IN.uv_MainTex);

  37. //要將顏色空間裡的法線[0,1],轉換至真正3D空間裡的法線範圍[-1,1]

  38. //注意:範圍基本都是從[0,1]轉換至[-1,1].主要是圖的通道與法線xyz的對應關係要根據法線貼圖格式而定

  39. //UnpackNormal, UnityCG.cginc裡的函式

  40. //o.Normal = UnpackNormal(packedNormal);

  41. //expand,標準法線解壓函式

  42. o.Normal = expand(packedNormal.xyz);

  43. }

  44. ENDCG

  45. }

  46. }


4.Rim Lighting 

  1. Shader "Example/Rim" {

  2. Properties {

  3. _MainTex ("Texture", 2D) = "white" {}

  4. _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 }

  5. SubShader {

  6. Tags { "RenderType" = "Opaque" }

  7. CGPROGRAM

  8. #pragma surface surf Lambert

  9. struct Input {

  10. float2 uv_MainTex;

  11. float2 uv_BumpMap; float3 viewDir; //3 };

  12. sampler2D _MainTex;

  13. sampler2D _BumpMap; float4 _RimColor;//4 float _RimPower;//5 void surf (Input IN, inout SurfaceOutput o) {

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

  15. 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 }

  16. ENDCG

  17. }

  18. Fallback "Diffuse"

新增的一些東西,我都用數字標註了。以下進行詳細解讀:

第一處(//1):上一個Color型別的顯示為Rim Color的變數。顏色值RGBA對應0.26,0.19,0.16,0.0

第二處(//2):這個Range型別的變數,結果還是一個float。只是這個float是在這個range之內。為什麼這麼定義呢。如果超多,或過小,則使用range內指明的值代替。

第四、五處(//4,//5):定義兩個變數對應properties裡的值,取出使用。

第六、七處:最裡層是Normalize函式,用於獲取到的viewDir座標轉成一個單位向量且方向不變,外面再與點的法線做點積。最外層再用saturate算出一[0,1]之間的最靠近(最小值但大於所指的值)的值。這樣算出一個rim邊界。為什麼這麼做。原理以下解釋:

=>看圖。

=>這裡o.Normal就是單位向量。外加Normalize了viewDir。因此求得的點積就是夾角的cos值。

=>因為cos值越大,夾角越小,所以,這時取反來。這樣,夾角越大,所反射上的顏色就越多。於是就得到的兩邊發光的效果。哈哈這樣明瞭吧。

這裡介紹一下這個half。CG裡還有類似的float和fixed。half是一種低精度的float,但有時也會被選擇成與float一樣的精度。fragment是一定會支援fixed型別,同時也會有可能將其精度設成與float一樣,這個比較複雜,後面篇章學到fragment時再深入探討。

以下為與3的對比,大家一下就知道誰是用了rim color的吧。對!下面那個盒子就是用些shader的效果。

5.Detail Texture

  1. Shader "Example/Detail" {

  2. Properties {

  3. _MainTex ("Texture", 2D) = "white" {}

  4. _BumpMap ("Bumpmap", 2D) = "bump" {} _Detail ("Detail", 2D) = "gray" {} }

  5. SubShader {

  6. Tags { "RenderType" = "Opaque" }

  7. CGPROGRAM

  8. #pragma surface surf Lambert

  9. struct Input {

  10. float2 uv_MainTex;

  11. float2 uv_BumpMap;

  12. float2 uv_Detail;

  13. };

  14. sampler2D _MainTex;

  15. sampler2D _BumpMap;

  16. sampler2D _Detail;

  17. void surf (Input IN, inout SurfaceOutput o) {

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

  19. o.Albedo *= tex2D (_Detail, IN.uv_Detail).rgb * 2;

  20. o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));

  21. }

  22. ENDCG

  23. }

  24. Fallback "Diffuse"

  25. }


這個最好理解了。

前面三個一樣。上一個2D Texture。

最後一個黑體:在原先的反射基礎上,在加一層,Texture的反射。

就是這樣啦。最後上幾個截圖,大家一定就明白。

6.Detail Texture in Screen Space

  1. Shader "Example/ScreenPos" {

  2. Properties {

  3. _MainTex ("Texture", 2D) = "white" {}

  4. _Detail ("Detail", 2D) = "gray" {}

  5. }

  6. SubShader {

  7. Tags { "RenderType" = "Opaque" }

  8. CGPROGRAM

  9. #pragma surface surf Lambert

  10. struct Input {

  11. float2 uv_MainTex; float4 screenPos; };

  12. sampler2D _MainTex;

  13. sampler2D _Detail;

  14. void surf (Input IN, inout SurfaceOutput o) {

  15. 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; }

  16. ENDCG

  17. }

  18. Fallback "Diffuse"

  19. }


這個比較有趣,是從上個例子的基礎上將第二層疊加上的2D Texture根據當前螢幕的UV進行疊加,而不是根據自身的UV。這樣帶有含此shader材質的物體的貼圖就會跟著移動到的位置而變換圖片。

這裡只需要說三點:

1.關於screenPos:screenPos是一個三維點,但是用齊次座標的形式表示出來就是(x,y,z,w),根據齊次座標的性質。(x,y,z,w)的齊次座標對應三維點(x/w,y/w,z/w)。因此把w值除掉可以看來是一種Normalize的作法,這樣就取出了實際的螢幕xy的UV值。

2.對screenUV進行倍剩:此處剩float2(8,6)意為將原獲取到螢幕尺寸進行拉大的倍數。即x軸拉大8倍,y軸拉大6倍。

3.如何就平鋪了剛好一行8個,一列6個了呢? 原因我覺得是在於2d Texture自己是按Normalize後進行鋪的,因此在//2(剛轉完標準的)screenPos後,將其剩多少即便將原圖鋪多少張。

OK。明瞭。其實這個東西可以拿來做放大鏡的應用。上圖:

7. Cubemap reflection

  1. Shader "Example/WorldRefl" {

  2. Properties {

  3. _MainTex (

  4. "Texture", 2D) = "white" {} _Cube ("Cubemap", CUBE) = "" {} }

  5. SubShader {

  6. Tags { "RenderType" = "Opaque" }

  7. CGPROGRAM

  8. #pragma surface surf Lambert

  9. struct Input {

  10. float2 uv_MainTex; float3 worldRefl; };

  11. sampler2D _MainTex; samplerCUBE _Cube; void surf (Input IN, inout SurfaceOutput o) {

  12. o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb * 0.5; o.Emission = texCUBE (_Cube, IN.worldRefl).rgb; }

  13. ENDCG

  14. }

  15. Fallback "Diffuse"

  16. }

  1. Shader "Example/WorldRefl Normalmap" {

  2. Properties {

  3. _MainTex ("Texture", 2D) = "white" {}

  4. _BumpMap ("Bumpmap", 2D) = "bump" {}

  5. _Cube ("Cubemap", CUBE) = "" {}

  6. }

  7. SubShader {

  8. Tags { "RenderType" = "Opaque" }

  9. CGPROGRAM

  10. #pragma surface surf Lambert

  11. struct Input {

  12. float2 uv_MainTex;

  13. float2 uv_BumpMap;

  14. float3 worldRefl; INTERNAL_DATA };

  15. sampler2D _MainTex;

  16. sampler2D _BumpMap;

  17. samplerCUBE _Cube;

  18. void surf (Input IN, inout SurfaceOutput o) {

  19. o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb * 0.5;

  20. o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));

  21. o.Emission = texCUBE (_Cube, WorldReflectionVector (IN, o.Normal)).rgb;

  22. }

  23. ENDCG

  24. }

  25. Fallback "Diffuse"

  26. }


這兩段都是加一個cubemap的反射。第二段相比之下是在有normal反射的基礎上加的。Cubemap這東西,可設定幾種面的不能渲染圖,這方面可用於做天空盒。因為這樣可以從各個角度看過去以顯示不同的渲染效果。

以下說明:

1. worldRefl:即為世界空間的反射向量。

2. texCUBE:將反射向量一個個的往_Cube反射盒上找出然後做為Emission反射出來。

3. 第二個例子只是將其用在Normal反射後,這樣一定要多新增一個INTERNAL_DATA的屬性,另外也需用到WorldReflectionVectore方法取其利用Normal後的反射向量值。

類似於的效果,可見官網中的。我這也有一個,有點像打了光的樣子。

8.Slices via World Space Position

  1. Shader "Example/Slices" {

  2. Properties {

  3. _MainTex ("Texture", 2D) = "white" {}

  4. _BumpMap ("Bumpmap", 2D) = "bump" {}

  5. }

  6. SubShader {

  7. Tags { "RenderType" = "Opaque" }

  8. Cull Off

  9. CGPROGRAM

  10. #pragma surface surf Lambert

  11. struct Input {

  12. float2 uv_MainTex;

  13. float2 uv_BumpMap;

  14. float3 worldPos;

  15. };

  16. sampler2D _MainTex;

  17. sampler2D _BumpMap;

  18. void surf (Input IN, inout SurfaceOutput o) {

  19. clip (frac((IN.worldPos.y+IN.worldPos.z*0.1) * 5) - 0.5); o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;

  20. o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));

  21. }

  22. ENDCG

  23. }

  24. Fallback "Diffuse"

  25. }


在看完這段後,我自己另外又加一段,以作對比:

  1. float3 _tWorldPos;

  2. void surf (Input IN, inout SurfaceOutput o) {

  3. _tWorldPos = IN.screenPos.xyz / IN.screenPos.w;

  4. //clip (frac((IN.worldPos.y+IN.worldPos.z*0.1) * 5) - 0.5);

  5. clip (frac((_tWorldPos.y+_tWorldPos.z*0.1) * 3) - 0.5);

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

  7. o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));

  8. }


第二個黑體:frac是取小數的函式,如1.23 取出來是 0.23。clip函式用於清Pixel的,負值情況下才進行清pixel。且越小,即絕對值越大則清越多。 這裡注意那個* 5,仔細一想,如果frac出來的值越大,-0.5值就越大,絕對值就越小,因此這樣清掉的pixel越少,所以就可以間接的增加分段的次數。那為什麼要+IN.worldPos.z*0.1呢,主要原因就是空開的斷新增一個傾斜角度,可以用空間思想想下。

我的那段,就是將要clip的座標換掉,換成螢幕的。這樣你移動物體時,clip掉的部分會變化。

最後,上下效果圖:

9.Normal Extrusion with Vertex Modifier

  1. Shader "Example/Normal Extrusion" {

  2. Properties {

  3. _MainTex ("Texture", 2D) = "white" {} _Amount ("Extrusion Amount", Range(-1,1)) = 0.5 }

  4. SubShader {

  5. Tags { "RenderType" = "Opaque" }

  6. CGPROGRAM

  7. #pragma surface surf Lambert vertex:vert //1

  8. struct Input {

  9. float2 uv_MainTex;

  10. }; float _Amount; //2 void vert (inout appdata_full v) { //3 v.vertex.xyz += v.normal * _Amount; //4 } sampler2D _MainTex;

  11. void surf (Input IN, inout SurfaceOutput o) {

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

  13. }

  14. ENDCG

  15. }

  16. Fallback "Diffuse"

  17. }


這是個自定義vertex的例子,效果可以實現點座標的放大縮小,以形成肥仔與瘦棍的效果,哈哈。

第一個黑體(//1):新增一個可選引數為vertex,主要是為了給其新增一個函式vert。

第二個黑體(//2):這個_Amount對應開頭的那個屬性_Amount。具體是個Range值,可在shader介面外通過滑動條改變這個值。預設為0.5。

第三個黑體(//3):這裡除了之前學過的東西外,多了個appdata_full的結構體。這裡面的結構(載自UNITY官方論壇)如下:

  1. struct appdata_full {

  2. float4 vertex : POSITION;

  3. float4 tangent : TANGENT;

  4. float3 normal : NORMAL;

  5. float4 texcoord : TEXCOORD0;

  6. float4 texcoord1 : TEXCOORD1;

  7. fixed4 color : COLOR;

  8. #if defined(SHADER_API_XBOX360)

  9. half4 texcoord2 : TEXCOORD2;

  10. half4 texcoord3 : TEXCOORD3;

  11. half4 texcoord4 : TEXCOORD4;

  12. half4 texcoord5 : TEXCOORD5;

  13. #endif

  14. };


第四個黑體(//4):就是像為個點,換當前法線向量的指定倍數進行擴充套件。

上效果:

10.Custom data computed per-vertex

  1. Shader "Example/Custom Vertex Data" {

  2. Properties {

  3. _MainTex ("Texture", 2D) = "white" {}

  4. }

  5. SubShader {

  6. Tags { "RenderType" = "Opaque" }

  7. CGPROGRAM

  8. #pragma surface surf Lambert vertex:vert struct Input {

  9. float2 uv_MainTex;

  10. float3 customColor; //1

  11. };

  12. void vert (inout appdata_full v, out Input o) {//2

  13. UNITY_INITIALIZE_OUTPUT(Input,o); //3

  14. o.customColor = abs(v.normal); //4

  15. }

  16. sampler2D _MainTex;

  17. void surf (Input IN, inout SurfaceOutput o) {

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

  19. o.Albedo *= IN.customColor; //5

  20. }

  21. ENDCG

  22. }

  23. Fallback "Diffuse"

  24. }

這個例子是用來渲染顏色的。我的分析如下:

第一處(//1):取一個顏色值,float3,對應RGB。

第二處(//2):較前個例子,多一個Input型別的引數,只為輸出使用。

第三處(//3):UNITY_INITIALIZE_OUTPUT(type,name)這個函式大有用處,主要是將叫[name]的變數請空改成type型別。以下是從HLSLSupport.cginc裡找到的定義:

  1. #if defined(UNITY_COMPILER_HLSL)

  2. #define UNITY_INITIALIZE_OUTPUT(type,name) name = (type)0;

  3. #else

  4. #define UNITY_INITIALIZE_OUTPUT(type,name)

  5. #endif

第四處(//4):RGB顏色值當然只能為正值,所以使用絕對值去取normal的值。

第五處(//5):在原先已經渲染上texture顏色值的基礎上,加上這層自定義的顏色值。

上效果:

11.Final Color Modifier

  1. Shader "Example/Tint Final Color" {

  2. Properties {

  3. _MainTex (

  4. "Texture", 2D) = "white" {}

  5. _ColorTint ("Tint", Color) = (1.0, 0.6, 0.6, 1.0) }

  6. SubShader {

  7. Tags { "RenderType" = "Opaque" }

  8. CGPROGRAM

  9. #pragma surface surf Lambert finalcolor:mycolor struct Input {

  10. float2 uv_MainTex;

  11. }; fixed4 _ColorTint; void mycolor (Input IN, SurfaceOutput o, inout fixed4 color) {

  12. color *= _ColorTint; }

  13. sampler2D _MainTex;

  14. void surf (Input IN, inout SurfaceOutput o) {

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

  16. }

  17. ENDCG

  18. }

  19. Fallback "Diffuse"

  20. }

這個例子是跟上面例子的對比,前種使用普通反射進行疊加上顏色,此處則是直接使用finalcolor對其顏色進行處理,這種可以處理整個模型的固定顏色值的渲染。以下做簡要的分析:

1.finalcolor:mycolor :這個是另一種可選引數,就是使用者自定義的顏色處理函式。函式名為mycolor.

2.mycolor函式:注意到函式除了有surf的兩個引數外,還多了個顏色引數,這個顏色引數就是當前模型上顏色物件,對他的更改將直接影響全部來自於lightmap,light probe和一些相關資源的顏色值。

效果:

12.Custom Fog with Final Color Modifier

  1. Shader "Example/Fog via Final Color" {

  2. Properties {

  3. _MainTex ("Texture", 2D) = "white" {} _FogColor ("Fog Color", Color) = (0.3, 0.4, 0.7, 1.0) }

  4. SubShader {

  5. Tags { "RenderType" = "Opaque" }

  6. CGPROGRAM

  7. #pragma surface surf Lambert finalcolor:mycolor vertex:myvert

  8. struct Input {

  9. float2 uv_MainTex;

  10. half fog;

  11. };

  12. void myvert (inout appdata_full v, out Input data)

  13. {

  14. UNITY_INITIALIZE_OUTPUT(Input,data);

  15. float4 hpos = mul (UNITY_MATRIX_MVP, v.vertex);//1

  16. data.fog = min (1, dot (hpos.xy, hpos.xy) * 0.1); //2

  17. }

  18. fixed4 _FogColor;

  19. void mycolor (Input IN, SurfaceOutput o, inout fixed4 color)

  20. {

  21. fixed3 fogColor = _FogColor.rgb;

  22. #ifdef UNITY_PASS_FORWARDADD //3

  23. fogColor = 0; //3

  24. #endif //3

  25. color.rgb = lerp (color.rgb, fogColor, IN.fog); //4 }

  26. sampler2D _MainTex;

  27. void surf (Input IN, inout SurfaceOutput o) {

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

  29. }

  30. ENDCG

  31. }

  32. Fallback "Diffuse"

  33. }

這個很高難度,裡面還有些之前沒用到過的函式,以下是我的理解:

第一處(//1):mul是矩陣相乘的函式。UNITY_MATRIX_MVP是model、view、projection三個矩陣相乘出來的4x4的矩陣。v.vertex是一個float4的變數,可理解成4x1的矩陣,兩者相乘,則得出一個float4,這個值就是視角視窗的座標值,這個座標就跟camera的關聯了。

第二處(//2):這個fog的浮點值就是其強度,範圍一般在-1到1之間,說一般,只是我個人建議的值,設成其他也行,只是沒多大意義。越負就越黑。再看後面這個點積,這個仔細一想,不難理解,其實就是為了達到一種擴散的效果,因此兩個一樣的向量相乘,其實就是直接對座標做平方擴充套件,這樣fog就更有霧的感覺。

第三處(//3):這個巨集不好找,就看官方對這個例子的解釋為正向渲染時的額外通道。字面不好理解,多多嘗試過可以有所發現,其實就是在霧氣漸漸消失處那塊額外的渲染區。可以將fogColor = 0; 改成fogColor = fixed3(1,0,0)。外面霧氣顏色再選成白色,效果則如下:


霧氣改成綠色後:效果如下:

第四處(//4):lerp函式是個有趣的函式。第一個引數是左邊界,第二個引數是右邊界,第三個相當於一個值介於0到1之間的遊標。遊標為0,則為左邊界,為1為右邊界,取中間值則是以此類推,取插值。其實也可以把它看成百分比。這裡的fog則可以看來那個遊標,值越大,則越接近fogColor,越小越接近原色。

原shader所出來的效果再來張:

13.Linear Fog

  1. Shader "Example/Linear Fog"{

  2. Properties {

  3. _MainTex ("Base (RGB)", 2D) = "white" {}

  4. }

  5. SubShader {

  6. Tags { "RenderType"="Opaque" }

  7. LOD 200 //1

  8. CGPROGRAM

  9. #pragma surface surf Lambert finalcolor:mycolor vertex:myvert

  10. sampler2D _MainTex;

  11. uniform half4 unity_FogColor; //2

  12. uniform half4 unity_FogStart;

  13. uniform half4 unity_FogEnd;

  14. struct Input {

  15. float2 uv_MainTex;

  16. half fog;

  17. };

  18. void myvert (inout appdata_full v, out Input data) {

  19. UNITY_INITIALIZE_OUTPUT(Input,data);

  20. float pos = length(mul (UNITY_MATRIX_MV, v.vertex).xyz); //3

  21. float diff = unity_FogEnd.x - unity_FogStart.x; //4

  22. float invDiff = 1.0f / diff; //5

  23. data.fog = clamp ((unity_FogEnd.x - pos) * invDiff, 0.0, 1.0); //6

  24. }

  25. void mycolor (Input IN, SurfaceOutput o, inout fixed4 color) {

  26. fixed3 fogColor = unity_FogColor.rgb;

  27. #ifdef UNITY_PASS_FORWARDADD

  28. fogColor = 0;

  29. #endif

  30. color.rgb = lerp (fogColor, color.rgb, IN.fog);

  31. }

  32. void surf (Input IN, inout SurfaceOutput o) {

  33. half4 c = tex2D (_MainTex, IN.uv_MainTex);

  34. o.Albedo = c.rgb;

  35. o.Alpha = c.a;

  36. }

  37. ENDCG

  38. }

  39. FallBack "Diffuse"

  40. }

這個官方只貼出了程式碼,無任何解釋。網上也未曾看到有人解答,在此為大家分析下。其實這個與上面那個例子相比之下,採用的fog的源頭變了,這裡是獲取rendersettings裡的fog來設定Fog的顏色、強度與起點終點等。以下進行解析:

第一處(//1):LOD 200,200是個代號,設成此的目的就是限制shader級別只到200為止,高過200的不採用,即使顯示卡支援,也不會使用高過200的shader級別的渲染方式。官方的解釋:http://docs.unity3d.com/Documentation/Components/SL-ShaderLOD.html

第二處(//2):此處標記uniform的意圖就是讓Cg可以使用此變數。因此這三個uniform變數均來自於RenderSetting中。你可以預先設定好三個值。

第三處(//3):length函式用於取一個向量的長度,如果是float3則採取如下形式:

  1. float length(float3 v)

  2. {

  3. return sqrt(dot(v,v));

  4. }


就是點積取平方根。

第四處(//4):計算fog起終點間的反差。

第五處(//5):將4中算得的diff置反過來。

第六處(//6):則將算出來的離視角的距離與0到1之間進行比對,小於0則為0,大於1則為1,範圍之內就是其原值,總的來說,利用clamp函式防止其出界。

分析下原理:咱們先將rendersetting裡的顏色設成紅色,fog start 設成0, fog end設成50。
這時算出的diff = 50, invdiff = 1/50。將原fog的計算稍做簡化,得出如下結果:

fog = clamp((1 - pos/50) , 0 , 1);這個式子很是明瞭,pos是距離,即距離越遠,clamp裡值越小,根據後面這句:

color.rgb = lerp (fogColor, color.rgb, IN.fog);

我們就可以判斷出其越靠近fogColor,霧氣就會越重。

最後上個效果圖:這裡選的是Linear的fog。

到此,所有的surface shader的官方例子都詳細的介紹完了。哎,開源中國對cg無什麼程式碼顯示支援,大家要程式碼看不清,可以直接去官網上去面看。

四、學習技巧

這裡是我個人的一些觀點:

1.遇問題先找官網,找官網論壇,找官網文件。

2.學會從軟體根目錄下的CGIncludes資料夾下找相關的函式巨集定義。

3.積累相關線性代數與計算機圖形學的知識,學習會更輕鬆些。

相關推薦

Unity shader 全方位學習

What?? Shader,看起來好高階的樣子,是的,這是Unity中高階進階的必備。因此,兄弟我就在此記下我學習官網的一些心得。 此為一。主要介紹些Surface Shaders的知識。具體的大家也可去官網(如下)學習。 一、概念篇 1.基準:unity裡的s

Linux系統學習之相關概念???

正是 range 不同的 struct pan 根據 inode 存在 opera “一切皆是文件”是 Unix/Linux 的基本哲學之一。不僅普通的文件,目錄、字符設備、塊設備、套接字等在 Unix/Linux 中都是以文件被對待;它們雖然類型不同,但是對其提供的卻是同

ext2系統學習—— 目錄磁盤結構

echo free 文件格式 htm file 目錄結構 bitmap 點號 name 創建鏡像、mount等操作和上一篇一樣,測試目錄結構如下: 一些文件系統信息如下: Block size: 1024 Inodes per group: 1

Linux系統學習之重要數據結構1

class targe html evel 系統結構 會有 集合 spec lan 轉載自:https://blog.csdn.net/wudongxu/article/details/6436894 《Linux內核設計與實現》 http://www.ibm.com/

liuux 管理命令

linux文件管理命令 楚天逸 於平 linux Linux 文件管理命令(整理版 一 )1.cat: 用於八檔案串聯接後傳到基本輸出. 使用權限:所有使用者 語法格式: cat [-AbeEnstTuv] [--help] [--

的操作

全部 strip() 文件的 () 讀一行 odi == class log 文件的使用: 打開——>操作——>關閉 ex1:讀和寫 1 #pycharm項目文件下,先創建文本文件my_heart_will_go_on 2 f=open(‘my_heart_w

安卓存儲

tostring rate 存儲 gen ide import ins troy ace package com.example.sql_file_Input; import java.io.BufferedWriter;import java.io.FileOutputS

JavaWeb(實現上傳)

submit tps pack 字符 title puts 安全 實現 servlet 通過Servlet來實現文件上傳的功能 實現用戶將文件上傳到服務裏的功能 文件上傳功能解釋: 當用戶在前端網頁點擊文件上傳後,javaWeb的servlet會獲得用戶所提交的文件並且

Sail.js官方閱讀筆記——總體結構

sails.js是一個後端開發框架,它是基於Node的著名框架express之上的。當前工作接手了一個系統,該系統的console部分是以sails.js完成其前後端功能的,故學習了sails.js的部分官方文件,特以此係列筆記以記之。因初學乍練,如有錯誤,歡迎指正。 在官網中,使用Sail

Drupal8[譯]——Overview

認識DP 這份手冊會帶你瞭解dp中各個概念的詳細描述,幫助你理解dp框架,協助你決定dp是否適合你的專案。 在安裝dp8之前,最好了解下dp是什麼、dp是如何工作的。相信我,這點時間花費的很值得。 Overview 介紹 dp是開源(免費)的,可以用來建站,但遠不止如此。

Unity Shader入門精要》自學筆記

Shader "Custom/zhudingdian" {Properties {_Diffuse("Diffuse",color) = (1,1,1,1)//用於顏色設定}SubShader {pass{tags{"LightMode" = "ForwardBase"}C

Java 集合系列16之 Spring Boot 配置 選項配置

writer) face 學習 apach .sql logs aspectj via threshold springboot配置選項(一) =================================================================

Memcache精選整理

MemCache全名可以看做MemoryCache。功能是做快取。為什麼不說他是一個快取資料庫呢,因為它很輕量,只是儲存一般的K-V鍵值對,相比Redis而言,Redis功能更多,比如分散式、持久化、支援的資料結構多這些特點而言,MemCache更簡單,只基於LruCache做快取。

【Maven】Optional & Exclusion Maven中的可選依賴和依賴性排除

本節介紹Maven中的可選依賴和依賴性排除功能。 【原文連結】 Optional 依賴 如果明確一個 project 無論出於什麼原因考慮都不可能繼續分割成子專案,則其依賴可以使用 optional。 如果其他 project 依賴了 使用 optional 的

yolov3 設計到編譯,訓練,map等引數測試,windows和linux均有說明

GitHub原文:https://github.com/AlexeyAB/darknet#how-to-compile-on-linux 參考部落格原址: https://blog.csdn.net/qq_34806812/article/details/813851

Zookeeper—第二章 2.概述-概述

ZooKeeper使用ACLs控制訪問ZooKeeper使用ACL來控制訪問znodes(Zookeeper資料樹上的資料節點). ACL的實現與UNIX的檔案訪問許可權十分相似: 它使用許可權bit來允許/拒絕一個節點和位元許可範圍的各種操作. 不同於傳統的UNITX許可權系統,一個Zookeeper節點沒

Jenkins 翻譯彙總

使用者手冊 使用 Jenkins 使用憑證 管理 Jenkins 配置系統 管理安全 管理工具 管理外掛 Jenkins CLI 命令列介面 Script Console 指令碼終端

[譯]webpack :指南 -- 7.程式碼分割

原創翻譯,轉載請註明出處。  原文地址:https://webpack.js.org/guides/code-splitting-import/ 動態引入 目前,一個把類函式的模組載入語法import()新增到ECMAScript的提議,正在討論中。 ES2015載入器細則定義import()為一個能在執

Objective-C 程式語言-屬性的宣告

Declared Properties The Objective-C declared properties feature provides a simple way to declare and implement an object’s accessor methods. Ov

Objective-C 程式語言十三 終結篇-詞彙表

Glossary abstract class   A class that’s defined solely so that other classes can inherit from it. Programs don’t use instances of an abstract class; the