【Unity Shader实战】利用凹凸纹理和边缘光模拟全息投影的效果

0x00 序言
项目需要模型在展示时,可以拥有类似全息投影的效果,类似一些科幻电影或者是高科技的场景。本文分析了如何利用凹凸纹理和边缘光来近似模拟这种效果。
0x01 效果图
【Unity Shader实战】利用凹凸纹理和边缘光模拟全息投影的效果_第1张图片
0x02 基本概念
本次的shader部分主要修改了surface shader,其中的一些基本概念:

  • SurfaceOutput是预定义的输出结构:
struct SurfaceOutput {
    half3 Albedo;     //纹理颜色(r,g,b)
    half3 Normal;     //法向量(x,y,z)
    half3 Emission;   //自发光颜色(r,g,b)
    half Specular;    //镜面反射度
    half Gloss;       //光泽度
    half Alpha;       //透明度
};
  • UnpackNormal
    UnpackNormal接受一个fixed4的输入,并将其转换为所对应的法线值(fixed3),并将其赋给输出的Normal。
o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
  • saturate( )函数

作用其实也就是将取值转化为[0,1]之内的一个值。如果 x 小于 0,返回 0;如果 x 大于 1,返回1;否则,返回 x

0x03 Shader代码
下面的代码算是强撸出来的,参考了网上一些资料。有些细节还不是很明了,希望能倒逼自己花时间把所有原理吃透。

  Shader "CoffeecatGame/Hologram" {
    Properties {
      _MainTex ("Texture", 2D) = "white" {}
      _BumpMap ("Bumpmap", 2D) = "bump" {}
      // 边缘颜色
        _RimColor("Rim Color", Color) = (0.26,0.7,1.0,0.0)
      // 边缘颜色强度
        _RimPower("Rim Power", Range(0.1,8.0)) = 3.0
      // 像素 颜色强度系数
        _RimIntensity("Rim Intensity", Range(0.1,8.0)) = 3.0
      // 条纹粗细
        _ClipPower("Clip Power", Range(0.0,301.0)) = 200.0
        _Brightness("Brightness",Range(0.0,30)) = 1.5
        _Alpha("Alpha", Range(0.0,1.0)) = 0.0
      // 条纹颜色插值系数
        _Crackle("Crackle", Range(0.0, 1.0)) = 0.0
    }
    SubShader {
      //"RenderType" = "Transparent"  半透明着色器
      //"Queue"="Transparent" 半透明物体渲染队列
      //"IgnoreProjector"="True"  忽略投影
      Tags { "RenderType" = "Transparent"  "Queue"="Transparent" "IgnoreProjector"="True"}

     Pass {
       //ZWrite On  深度写入 开启
       //ColorMask 0 设置颜色写遮罩 关闭所有颜色通道渲染
        ZWrite On
        ColorMask 0
       }

      CGPROGRAM
      //Lambert,声明了表面着色器,并指定了光照模型。
      #pragma surface surf Lambert alpha 
      struct Input {
          float2 uv_MainTex;
          float2 uv_BumpMap;
          float3 viewDir;
          float3 worldPos;    
          float4 screenPos;      
      };
      sampler2D _MainTex;
      sampler2D _BumpMap;

      float4 _RimColor;
      float _RimPower;
      float _ClipPower;
      float _Brightness;
      float _Alpha;
      float _RimIntensity;
      float _Crackle;

      void surf (Input IN, inout SurfaceOutput o) 
      {
          float2 screenUV = IN.screenPos.xy / IN.screenPos.w;
          // 扩散系数
          float _DiffuseAmount = 1;
          // 在贴图颜色与RimColor之间做插值
          half4 basecol = lerp(tex2D(_MainTex, IN.uv_MainTex), _RimColor, _Crackle);
          // 获取贴图颜色对应的灰度图
          half3 graycol = dot(basecol.rgb, float3(0.3, 0.59, 0.11));
          // frac 返回小数部分
          if (frac(screenUV.y * _ClipPower) - 0.5 > 0)
            _DiffuseAmount = 1;
          else 
            _DiffuseAmount = 0;

          if (_ClipPower == 0)
          {
            _DiffuseAmount = 1;
          }
          // 像素的颜色 
          o.Albedo = lerp(graycol, basecol, _DiffuseAmount);
          // 从凹凸纹理提取法线信息
          o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
          // 从_RimColor参数获取自发光颜色(_Brightness是增强因子)
          half rim = 1.0 - saturate(dot (normalize(IN.viewDir), o.Normal));
          o.Emission = lerp(_RimColor.rgb * pow (rim, _RimPower) * _Brightness, basecol, _DiffuseAmount) * _Brightness;

          o.Alpha = _Alpha;
      }
      ENDCG
    } 
    Fallback "Diffuse"
  }

0x04 参考资料
【浅墨Unity3D Shader编程】之六 暗黑城堡篇: 表面着色器(Surface Shader)的写法(一)

你可能感兴趣的:(Unity3d,Shader)