Surface Shader


Surface Shader:
  (1)必須放在SubShdader塊,不能放在Pass內部;
  (2)#pragma sufrace surfaceFunction lightModel [optionalparams]
  (3)格式
  CG規定了聲明為表面着色器的方法(就是我們這里的surf)的參數類型和名字,因此我們沒有權利決定surf的輸入輸出參數的類型,只能按照規定寫。這個規定就是第一個參數是一個Input結構,第二個參數是一個inout的SurfaceOutput結構。
  struct SurfaceOutput {
    half3 Albedo; //像素的顏色
    half3 Normal; //像素的法向值
    half3 Emission; //像素的發散顏色
    half Specular; //像素的鏡面高光
    half Gloss; //像素的發光強度
    half Alpha; //像素的透明度
  };
  sampler2D就是GLSL中的2D貼圖的類型,相應的,還有sampler1D,sampler3d,samplerCube等等格式。而具體地想知道像素與坐標的對應關系,以及獲取這些數據,我們總不能一次一次去自己計算內存地址或者偏移,因此可以通過sampler2D來對貼圖進 行操作。
  在CG程序中,我們有這樣的約定,在一個貼圖變量(在我們例子中是_MainTex)之前加上uv兩個字母,就代表提取它的uv值(其實就是兩個代表貼圖上點的二維坐標 )。

  Input 這個輸入結構通常擁有着色器需要的所有紋理坐標(texture coordinates)。紋理坐標(Texturecoordinates)必須被命名為“uv”后接紋理(texture)名字。(或者uv2開始,使用第二紋理坐標集)。
  可以在輸入結構中根據自己的需要,可選附加這樣的一些候選值:
  float3 viewDir - 視圖方向( view direction)值。為了計算視差效果(Parallax effects),邊緣光照(rim lighting)等,需要包含視圖方向( view direction)值。
  float4 with COLOR semantic -每個頂點(per-vertex)顏色的插值。
  float4 screenPos - 屏幕空間中的位置。 為了反射效果,需要包含屏幕空間中的位置信息。比如在Dark Unity中所使用的 WetStreet着色器。
  float3 worldPos - 世界空間中的位置。
  float3 worldRefl - 世界空間中的反射向量。如果表面着色器(surface shader)不寫入法線(o.Normal)參數,將包含這個參數。 請參考這個例子:Reflect-Diffuse 着色器。
  float3 worldNormal - 世界空間中的法線向量(normal vector)。如果表面着色器(surface shader)不寫入法線(o.Normal)參數,將包含這個參數。
  float3 worldRefl; INTERNAL_DATA - 世界空間中的反射向量。如果表面着色器(surface shader)不寫入法線(o.Normal)參數,將包含這個參數。為了獲得基於每個頂點法線貼圖( per-pixel normal map)的反射向量(reflection vector)需要使用世界反射向量(WorldReflectionVector (IN, o.Normal))。
  float3 worldNormal; INTERNAL_DATA -世界空間中的法線向量(normal vector)。如果表面着色器(surface shader)不寫入法線(o.Normal)參數,將包含這個參數。為了獲得基於每個頂點法線貼圖( per-pixel normal map)的法線向量(normal vector)需要使用世界法線向量(WorldNormalVector (IN, o.Normal))。

vertex shader modifier:Surface Shader還可以單獨指定一個vertex shader,用於做一些運算,vertex函數擁有固定的輸入參數 inout appdata_full 。下面的例子通過vertex shader對頂點在發現方向上做了一些偏移:

    #pragma surface surf Lambert vertex:vert
    void vert (inout appdata_full v) 
    {
         v.vertex.xyz += v.normal * _Amount;
    }

   也可在vertex shader中計算自定義變量(Input中的這些變量不能以uv開頭,否則會出錯),這個計算結果會逐像素的傳遞到surface shader, 如下: 

復制代碼
Shader "James/Surface/CustomData"
{ 
    Properties 
    {
        _MainTex ("Base (RGB)", 2D) = "white" {}
    }
    SubShader 
    {
        Tags { "RenderType"="Opaque" }
        LOD 200
        
        CGPROGRAM
        #pragma surface surf Lambert vertex:vert

        sampler2D _MainTex;

        struct Input 
        {
            float2 uv_MainTex;
            float3 customColor; // 自定義數據
        };
        
        void vert(inout appdata_full, out Input o)
        {
            UNITY_INITIALIZE_OUTPUT(Input, o);
            o.customColor = abs(v.normal); // 在vs中計算自定義數據
        }

        void surf (Input IN, inout SurfaceOutput o) 
        {
            clip(frac((IN.worldPos.y + IN.worldPos.z * 0.1) * 5) - 0.5);
            o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb * IN.customColor;
            o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
        }
        ENDCG
    } 
    FallBack "Diffuse"
}
復制代碼

final color modifier: 編譯指令為finalcolor:functionName,函數接收三個參數 Input IN, SurfaceOutput o, inout fixed4 color 
  final color會影響渲染的最終顏色,它在所有的計算的最后進行影響,比如lightmap、lightprobe等產生的顏色也會受此函數影響。
  final color可以用來實現fog,fog只影響渲染的rgb,而不影響alpha,fog的原理是讓物體在最終的rgb和fog.color之間根據距離進行過度。

復制代碼
Shader "James/Surface Shader/Final Color Fog Liner" {
  Properties {
    _MainTex ("Base (RGB)", 2D) = "white" {}
  }
  SubShader {
    Tags { "RenderType"="Opaque" }
    LOD 200
    
    CGPROGRAM
    #pragma surface surf Lambert finalcolor:mycolor vertex:myvert

    sampler2D _MainTex;
    uniform half4 unity_FogColor;
    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);
      float diff = unity_FogEnd.x - unity_FogStart.x;
      float invDiff = 1.0f / diff;
      data.fog = clamp ((unity_FogEnd.x - pos) * invDiff, 0.0, 1.0);
    }
    // final color處理函數
    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"
}
復制代碼

  uniform:用於指定變量的數據初始化方式。 Uniform inputs,表示一些與三維渲染有關的離散信息數據,這些數據通常由應用程序傳入,並通常不會隨着圖元信息的變化而變化,如材質對光的反射信息、運動矩陣等。Uniform 修辭一個參數,表示該參數的值由外部應用程序初始化並傳入。
  使用Uniform 修辭的變量,除了數據來源不同外,與其他變量是完全一樣的。需要注意的一點是:uniform 修辭的變量的值是從外部傳入的,所以在Cg 程序(頂點程序和片段程序)中通常使用uniform 參數修辭函數形參,不容許聲明一個用uniform 修辭的局部變量!

Lighting model:光照模式,在編寫surface shader時,我們只需要描述各種屬性即可,真正的光照計算是Lighting mode負責的。
  內置光照模式:Lambert(diffuse lighting)、BlinnPhong(specular lighting),源碼在unity install path}/Data/CGIncludes/Lighting.cginc目錄。
  Lighting model其實就是按照指定規范編寫的一堆cg/hlsl,我們也可以定義自己的Lighting model。

  自定義光照模式:
  Surface Shader Lighting Models其實就是一些函數,這些函數我們也可以自己來寫。
  下面的例子就是一個自定義的Lambert光照模式,並在此基礎上實現的Toon效果:

Shader "James/Surface Shader/Diffuse" {
    Properties {
        _MainTex ("Base (RGB)", 2D) = "white" {}
        _RampTex ("Base (RGB)", 2D) = "white" {}
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200
        
        CGPROGRAM
        #pragma surface surf SimpleLambert

        sampler2D _RampTex;
        
        half4 LightingSimpleLambert(SurfaceOutput s, half3 lightDir, half atten)
        {
            half NdotL = dot(s.Normal, lightDir);
            // [-1, 1] --> [0, 1]
            half diff = NdotL *0.5 + 0.5;
            half3 ramp = tex2D(_RampTex, float2(diff, 0.5)).rgb;
            half4 c;
            c.rgb = s.Albedo * _LightColor0.rgb * ramp * (atten * 2);
            // c.rgb = s.Albedo * _LightColor0.rgb * (diff * atten * 2);
            c.a = s.Alpha;
            return c;
        }
        
        sampler2D _MainTex;

        struct Input {
            float2 uv_MainTex;
        };

        void surf (Input IN, inout SurfaceOutput o) 
        {
            o.Albedo = tex2D (_MainTex, IN.uv_MainTex);
        }
        ENDCG
    } 
    FallBack "Diffuse"
}

  下面的代碼表示了一個簡單的BlinnPhong的模式:

Shader "James/Surface Shader/SimpleBlinnPhong" {
    Properties {
        _MainTex ("Base (RGB)", 2D) = "white" {}
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200
        
        CGPROGRAM
        #pragma surface surf SimpleSpecular

        half4 LightingSimpleSpecular(SurfaceOutput s, half3 lightDir, half3 viewDir, half atten)
        {
            half3 h = normalize(lightDir + viewDir);
            half diff = max(0, dot(s.Normal, lightDir));
            
            float nh = max(0, dot(s.Normal, h));
            float spec = pow(nh, 48.0);
            
            half4 c;
            c.rgb = (s.Albedo * _LightColor0.rgb * diff + _LightColor0.rgb * spec) * (atten * 2);
            c.a = s.Alpha;
            return c;
        }
        
        sampler2D _MainTex;

        struct Input {
            float2 uv_MainTex;
        };

        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"
}

  再下面是關於lightmap的光照模式代碼,並在光照貼圖的基礎是添加了一個自定義顏色的實現:

Shader "James/Surface Shader/SimpleLightmap" {
        Properties {
            _MainTex ("Texture", 2D) = "white" {}
            _SelfColor("SceneCooor", Color) = (0.5, 0, 0, 1)
        }
        SubShader {
            Tags { "RenderType" = "Opaque" }
            CGPROGRAM

            #pragma surface surf Standard
            
            half4 _SelfColor;

            half4 LightingStandard (SurfaceOutput s, half3 lightDir, half atten) {
                half NdotL = dot (s.Normal, lightDir);
                half4 c; 
                c.rgb = s.Albedo * _LightColor0.rgb * (NdotL * atten * 2);
                c.a = s.Alpha;
                return c;
            }

            // 給lightmap乘一個自定義顏色
            inline half3 ColorLight (half3 i) {
                return _SelfColor.rgb * i;
            }
            
            inline fixed4 LightingStandard_SingleLightmap (SurfaceOutput s, fixed4 color) {
                half3 lm = ColorLight(DecodeLightmap (color));
                return fixed4(lm, 0);
            }

            inline fixed4 LightingStandard_DualLightmap (SurfaceOutput s, fixed4 totalColor, fixed4 indirectOnlyColor, half indirectFade) {
                half3 lm = ColorLight(lerp (DecodeLightmap (indirectOnlyColor), DecodeLightmap (totalColor), indirectFade));
                return fixed4(lm, 0);
            }

            inline fixed4 LightingStandard_StandardLightmap (SurfaceOutput s, fixed4 color, fixed4 scale, bool surfFuncWritesNormal) {
                UNITY_DIRBASIS

                half3 lm = ColorLight(DecodeLightmap (color));
                half3 scalePerBasisVector = DecodeLightmap (scale);

                if (surfFuncWritesNormal)
                {
                    half3 normalInRnmBasis = saturate (mul (unity_DirBasis, s.Normal));
                    lm *= dot (normalInRnmBasis, scalePerBasisVector);
                }

                return fixed4(lm, 0);
            }

            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"
    }

還有一些相關的編譯選項:

  #pragma debug  查看生成的代碼
  #pragma only_renderers: d3d9 只支持特定平台
  noforwardadd
  nolightmap
  exclude_path:prepass 不支持deferred模式

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM