实现一个玻璃效果的着色器

玻璃是一个非常复杂的材质;没必要对它刚到惊讶,在第四章,向PBR中添加透明度 这个知识点中,通过行为驱动开发创建测试用例和编写场景(Creating Test Cases and Writing Scenarios for Behavior Driven Development in Symfony) 我们已经创建了一个这样的着色器来模拟它了。然而,透明度没有办法复现玻璃的扭曲效果。大部分的玻璃自身是不完美的,所以当我们再看玻璃的时候会有扭曲效果。这个知识点我们将教你如何实现这样的效果。这个效果背后的思路是使用顶点和片元着色器以及抓取通道,然后对抓取纹理做一些修改并应用到它的UV数据中,从而实现扭曲效果。你可以从下面的图中看到效果,使用的是Unity标准资源库 (Unity Standard Assets) 中的玻璃染色纹理:
diagram


  • 始前准备
    这个知识点的步骤跟前一章中的有点像:
    1. 创建一个新的顶点和片元着色器。你可以复制前一个知识点 抓取通道 的着色器作为基础。
    2. 创建一个材质,用来承载着色器。
    3. 将材质球赋值给一个 quad,也可以是其他的扁平的几何图形,用来模拟玻璃。
    4. 然后再这个模拟的玻璃后面放一些其他的游戏物体,好观察扭曲效果。

  • 操作步骤【原书有错,下面是纠正后的步骤和代码】
    我们开始编辑顶点和片元着色器:
    1. 向着色器的属性快 ( Properties block) 中添加4个属性:
    Properties
    {
      _MainTex("Base (RGB) Trans (A)", 2D) = "white" {}
      _Colour("Colour", Color) = (1,1,1,1)
      _BumpMap("Noise text", 2D) = "bump" {}
      _Magnitude("Magnitude", Range(0,1)) = 0.05
    }
    
    1. 在Pass通道中添加下面的这些变量
    sampler2D _MainTex;
    sampler2D _BumpMap;
    float _Magnitude;
    sampler2D _GrabTexture;
    fixed4 _Colour;
    
    1. 将下面的纹理信息添加到输入和输出结构体中:
    struct vertInput 
      {
      float4 vertex : POSITION;
      float4 color : COLOR;
      float2 texcoord : TEXCOORD0;
    };
    struct vertOutput 
    {
      float4 vertex : SV_POSITION;
      float4 color : COLOR;
      float2 texcoord : TEXCOORD0;
      float4 uvgrab : TEXCOORD1;
      };
    
    1. 将UV数据从输入结构体赋值到输出结构体中:
    vertOutput vert(vertInput input) 
    {
      vertOutput o;
      o.vertex = UnityObjectToClipPos(input.vertex);
      o.color = input.color;
      o.texcoord = input.texcoord;
      o.uvgrab = ComputeGrabScreenPos(o.vertex);
      return o;
    }
    
    1. 使用下面的片元函数:
    half4 frag(vertOutput i) : COLOR
    {
      half4 mainColour = tex2D(_MainTex, i.texcoord);
      half4 bump = tex2D(_BumpMap, i.texcoord);
      half2 distortion = UnpackNormal(bump).rg;
      i.uvgrab.xy += distortion * _Magnitude;
      fixed4 col = tex2Dproj(_GrabTexture, UNITY_PROJ_COORD(i.uvgrab));
      return col * mainColour * _Colour;
      }
    
    1. 因为这个材质是透明的,所以我们还需要在它的 SubShader 块中改变它的 标签(tags)
    Tags{ "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Opaque" }
    
    1. 接下的工作就是为玻璃设置纹理和法线贴图从而替换掉抓取纹理。

  • 原理介绍
    该着色器的核心作用是使用抓取通道来获取已经被渲染在屏幕上的东西。我们在片元函数中实现了扭曲效果的。在这里法线贴图被解析并且用来计算抓取纹理的UV数据偏移:
    half4 bump = tex2D(_BumpMap, i.texcoord);
    half2 distortion = UnpackNormal(bump).rg;
    i.uvgrab.xy += distortion * _Magnitude;
    
    _Magnitude 这个滑动条用来控制效果的强弱。

  • 额外内容
    这个效果非常的通用;它可以基于法线贴图,通过抓取屏幕来创建扭曲效果。如果想模拟一些更有趣的效果没理由不使用它。有很多游戏会在爆炸中或者一些科幻设备上使用扭曲效果。这个材质也可以应用到球体中,如果使用不同的法线贴图,它还可以很好的模拟爆炸中的冲击波。