Shader學習筆記(一):Surface Shader

Surface Shader是Unity爲了方便shader編寫提供的特殊功能,它對底層的vertex/fragment shader做了封裝,省去了一些重複代碼編寫的工作量。我的理解是它同時具有vertex/fragment shader的功能,只是寫法上更加簡潔,更容易上手。

Unity的官方manual上就提供了幾個最好的學習例子,我在學習的過程中加上了註釋。

簡單的漫反射(Simple)

這裏寫圖片描述

  Shader "Example/Diffuse Simple" {
    SubShader {
      Tags { "RenderType" = "Opaque" } // 標籤,決定什麼時候渲染(對不透明物體渲染)
      CGPROGRAM // //CG語言標記開始
      #pragma surface surf Lambert // 編譯指令 surface shader 自定義函數 光照模型[1] 
      struct Input {               // 輸入的結構體
          float4 color : COLOR;    // 顏色值
      };
      void surf (Input IN, inout SurfaceOutput o) { // surface shader處理函數
          o.Albedo = 1;  // 將基礎顏色設爲白色[2]
      }
      ENDCG
    }
    Fallback "Diffuse" // 發生異常時回滾成Unity內置的Diffuse shader
  }

帶紋理的漫反射(Texture)

這裏寫圖片描述

  Shader "Example/Diffuse Texture" {
    Properties {
      _MainTex ("Texture", 2D) = "white" {} // 紋理,若沒有賦值則默認爲全白
    }
    SubShader {
      Tags { "RenderType" = "Opaque" }
      CGPROGRAM
      #pragma surface surf Lambert
      struct Input {
          float2 uv_MainTex; // 紋理uv座標
      };
      sampler2D _MainTex;
      void surf (Input IN, inout SurfaceOutput o) {
          o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; // 按uv座標查找紋理上的像素,並獲取rgb值
      }
      ENDCG
    } 
    Fallback "Diffuse"
  }

帶法向貼圖的漫反射(Normal mapping)

這裏寫圖片描述

Shader "Example/Diffuse Bump" {
    Properties {
      _MainTex ("Texture", 2D) = "white" {}
      _BumpMap ("Bumpmap", 2D) = "bump" {} // 法向貼圖[3]
    }
    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)); // 將從法向貼圖中取得的法向量賦給output
      }
      ENDCG
    } 
    Fallback "Diffuse"
  }

邊緣光(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) // 邊緣光的顏色
      _RimPower ("Rim Power", Range(0.5,8.0)) = 3.0 // 邊緣光的強度
    }
    SubShader {
      Tags { "RenderType" = "Opaque" }
      CGPROGRAM
      #pragma surface surf Lambert
      struct Input {
          float2 uv_MainTex;
          float2 uv_BumpMap;
          float3 viewDir; // 觀察向量
      };
      sampler2D _MainTex;
      sampler2D _BumpMap;
      float4 _RimColor;
      float _RimPower;
      void surf (Input IN, inout SurfaceOutput o) {
          o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
          o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
          // 對觀察向量和法向量求點積,這個值越小,代表這兩個方向夾角越接近90度,即爲輪廓邊緣
          half rim = 1.0 - saturate(dot (normalize(IN.viewDir), o.Normal)); 
          // 越接近邊緣,發出的光越亮
          o.Emission = _RimColor.rgb * pow (rim, _RimPower);
      }
      ENDCG
    } 
    Fallback "Diffuse"
  }

細節紋理(Detail Texture)

這裏寫圖片描述

  Shader "Example/Detail" {
    Properties {
      _MainTex ("Texture", 2D) = "white" {}
      _BumpMap ("Bumpmap", 2D) = "bump" {}
      _Detail ("Detail", 2D) = "gray" {} // 細節紋理[4]
    }
    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"
  }

屏幕空間下的細節紋理(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;
          screenUV *= float2(8,6);
          // 根據屏幕的uv座標來疊加細節紋理[5]
          o.Albedo *= tex2D (_Detail, screenUV).rgb * 2;
      }
      ENDCG
    } 
    Fallback "Diffuse"
  }

立方體貼圖反射(Cubemap Reflection)

這裏寫圖片描述

  Shader "Example/WorldRefl" {
    Properties {
      _MainTex ("Texture", 2D) = "white" {}
      _Cube ("Cubemap", CUBE) = "" {} // 立方體貼圖[6]
    }
    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;
          // 根據世界反射向量和立方體貼圖,反射相應的rgb
          o.Emission = texCUBE (_Cube, IN.worldRefl).rgb;
      }
      ENDCG
    } 
    Fallback "Diffuse"
  }

世界座標下的切片(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) {
      // 自定義的切片函數(將yz作爲參數,意味沿x軸做切片)
          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"
  }

使用頂點修改器的法線擠壓(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 // 聲明會使用頂點修改器
      struct Input {
          float2 uv_MainTex;
      };
      float _Amount;
      void vert (inout appdata_full v) { // 頂點修改器實現
          v.vertex.xyz += v.normal * _Amount; // 沿法線移動頂點的座標
      }
      sampler2D _MainTex;
      void surf (Input IN, inout SurfaceOutput o) {
          o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
      }
      ENDCG
    } 
    Fallback "Diffuse"
  }

逐頂點計算自定義數據(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; // 自定義數據
      };
      void vert (inout appdata_full v, out Input o) {
          UNITY_INITIALIZE_OUTPUT(Input,o);
          // 在頂點修改器中,將法向量的絕對值賦值給參數customColor
          o.customColor = abs(v.normal); 
      }
      sampler2D _MainTex;
      void surf (Input IN, inout SurfaceOutput o) {
          o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
          // 使用傳遞過來的參數,對顏色做疊加
          o.Albedo *= IN.customColor;
      }
      ENDCG
    } 
    Fallback "Diffuse"
  }

註釋:
[1]:Unity中最常用的是兩種內置的光照模型:LambertBlinnPhong
[2]:Albedo是物體的基礎顏色,與之不同的Emission是自發光顏色,詳細的input和output變量表參見官網
[3]:法向貼圖是爲了實現凹凸不平的效果,可參見文章
[4]:細節紋理是爲了讓紋理產生更細膩的感覺,可參見文章
[5]:注意rgb相乘和相加的應用場合不同,這裏給出瞭解釋。
[6]:立方體貼圖可用來做環境反射和天空盒,可參見文章

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章