UnityShader官方案例之表面着色器光照示例

“Without purpose, the days would have ended, as such days always end, in disintegration.”

这个系列是表面着色器的光照模型,以及对自定义光照模型的深入
编写表面着色器时,您是在描述一个表面的属性(反射率颜色、法线等等),并由光照模型完成光照交互的计算。内置光照模型为 Lambert(漫反射光照)和 BlinnPhong(高光光照)。

有时您可能想使用自定义光照模型,在表面着色器 (Surface Shader) 中这是可以实现的。光照模型不过是与一些惯例匹配的几个 Cg/HLSL 函数。
内置
Lambert

BlinnPhong模型
在 Unity 的
Lighting.cginc里都可以找到

其在正向渲染路径中用于非与视线方向相关的光照模型(例如,漫反射)。

half4 LightingName (SurfaceOutput s, half3 lightDir, half atten);

其在正向渲染路径中用于与视线方向相关的光照模型。

half4 LightingName (SurfaceOutput s, half3 lightDir, half3 viewDir, half atten);

其用于延时光照路径中。

half4 LightingName_PrePass (SurfaceOutput s, half4 light);

_PrePass 这个函数,所有使用它的着色器都将仅编译成正向渲染。

现在来解释光照模型的定义:必须是Lighting + Name格式
光照模型函数参数:lightDir是光照模型的方向,atten是光照模型的衰减系数,viewDir是摄像机的视线方向

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

step1
这是Unity3D内置的Lambert(漫反射)光照模型

Lambert光照模型就在Lighting.cginc中,现在用unity Editor打开就可以看到内置的漫反射光照函数是怎么写的了
UnityShader官方案例之表面着色器光照示例_第1张图片

现在,让我们进行一次完全相同的操作,但要编写出我们自己的光照模型,而不是使用内置的 Lambert 模型。

//
// {
//    Properties 
//    {
//      _MainTex ("Texture", 2D) = "white" {}
//    }
//
//    SubShader
//    {
//      Tags { "RenderType" = "Opaque" }
//      CGPROGRAM
//
      //光照模式声明:使用自定义的光照模式  
      #pragma surface surf SimpleLambert

      //光照函数名必须是Lighting + 自定义名字
      //它通过在表面法线(s.Normal)和光线方向(LightDir)之间执行一次数量积来计算光照
      //然后应用光衰减和颜色。
      half4 LightingSimpleLambert (SurfaceOutput s, half3 lightDir, half atten) 
      {
          //SurfaceOutput s这个就是经过表面计算函数surf处理后的输出,我们讲对其上的点根据光线进行处理
          //点积的结果在-1至1之间,这个值越大表示法线与光线间夹角越小,这个点也就应该越亮。余弦值计算不多说了
          half NdotL = dot (s.Normal, lightDir);
          half4 c;

          //接下来我们将surf输出的颜色(s.Albedo)与光线的颜色_LightColor0.rgb的乘积,
          //然后再与刚才计算的光强系数(Ndotl)和输入的衰减系数相乘,
          //为什么后面还要乘以2? 大家可以试试把乘以2去掉,其实就是为了进行光强补偿
          c.rgb = s.Albedo * _LightColor0.rgb * (NdotL * atten * 2);
          c.a = s.Alpha;
          return c;
      }

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

step2
下面进行改进,漫反射遮蔽

 #pragma surface surf WrapLambert

        half4 LightingWrapLambert (SurfaceOutput s, half3 lightDir, half atten) 
        {
          half NdotL = dot (s.Normal, lightDir);


          //在光照的基础上加上这句,增加光强
          //其实官方的说法更准确:其中照明“环绕”在对象边缘。它对模拟子面散射效果有用。
          half diff = NdotL * 0.5 + 0.5;
          half4 c;
          c.rgb = s.Albedo * _LightColor0.rgb * (diff * atten * 2);
          c.a = s.Alpha;
          return c;
        }

step3
卡通渐变

通过一个不同的渐变纹理 实现不同的效果,
这是一个“渐变”(Ramp) 光照模型,它使用纹理渐变来定义表面如何对光线与法线之间的夹角作出反应。

Shader"Example / AutisticPatient SurfaceShader001"
 {
    Properties 
    {
      _MainTex ("Texture", 2D) = "white" {}
      //渐变纹理,初始值为gray
      _Ramp("Ramp",2D) = "gray"{}
    }

    SubShader
    {
      //子着色器标签
      Tags { "RenderType" = "Opaque" }
      CGPROGRAM

      //光照模式声明:使用自定义的光照模式  
      #pragma surface surf Ramp

      //变量声明
      sampler2D _Ramp;

      //实现光照函数
      half4 LightingRamp (SurfaceOutput s, half3 lightDir, half atten) 
      {
          //点乘反射光线法线和光线方向
          half NdotL = dot (s.Normal, lightDir);

          //增强光强
          half diff = NdotL * 0.5 + 0.5;

          //从纹理中定义渐变效果,官方的例子中是float2(diff)这样写的,很明显报错了
          half3 ramp = tex2D (_Ramp, float2(diff,diff)).rgb;
          half4 c;
          c.rgb = s.Albedo * _LightColor0.rgb * ramp * (atten * 2);
          c.a = s.Alpha;
          return c;
       }

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

没有效果图,如果大家想找模型看看效果的话,asset store是个好地方,有很多免费的模型可以用,但一定要找有对应贴图的

step4
**简单高光**specular lighting model

Shader"Example / AutisticPatient SurfaceShader001"
 {
    Properties 
    {
      _MainTex ("Texture", 2D) = "white" {}
      //渐变纹理,初始值为gray
      _Ramp("Ramp",2D) = "gray"{}
    }

    SubShader
    {
      //子着色器标签
      Tags { "RenderType" = "Opaque" }
      CGPROGRAM

      //定义光照模型
      #pragma surface surf SimpleSpecular

      //实现自定义光照模型
      half4 LightingSimpleSpecular (SurfaceOutput s, half3 lightDir, half3 viewDir, half atten) 
      { 
          //normalize是来自CG语言中的函数,作用是归一化向量。
          half3 h = normalize (lightDir + viewDir);

          //点积后取最大值,当法线方向和灯光方向一致时,它们的点积结果达到最大值1,最亮
          //当两者方向垂直时,达到0,大于90度将为负值,max函数的作用便是保证光强不为负
          half diff = max (0, dot (s.Normal, lightDir));

          float nh = max (0, dot (s.Normal, h));

          //nh的 48次方
          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;
       }

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

这里面有些数学计算我没注释,大家可以改动其中的一些值,在unity 中进行编译查看效果,就会明白数学计算的作用

好了,这节就到这吧

你可能感兴趣的:(unity3d特效)