《Unity Shader入门精要》十一、让画面动起来

11.1 Unity Shader 中的内置变量

11.2 纹理动画

纹理动画在游戏中应用非常广泛。尤其在各种资源紧张的移动平台上,往往会使用纹理动画来代替复杂的粒子系统来模拟各种动效

11.2.1 序列帧动画

  • 优点:灵活性强,效果细腻
  • 缺点:美术工作量大

实现步骤:

  1. 建材质,建 Shader,将 Shader 赋给材质
  2. 场景中创建四边形(Quad),将它正对摄像机,将材质赋给它
  3. Shader 代码:(源码
    • 设置 Pass 相关状态,以渲染透明效果

      js 复制代码
      SubShader
      {
          Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
      
          Pass
          {
              Tags {"LightMode"="ForwardBase" }
      
              ZWrite Off
              Blend SrcAlpha OneMinusSrcAlpha
      • 序列帧图像包含了透明通道,因此可被当成一个半透明对象
        • Queue 和 RenderType 都设置成 Transparent
        • IgnoreProjector 设置为 True
        • 在 Pass 中使用 Blend 命令来开启并设置混合模式
        • ZWrite Off:关闭深度写入
    • 片元着色器

      js 复制代码
      fixed4 frag (v2f i) : SV_Target
      {
          // 根据时间和速度计算出帧图像的行列索引
          float time = floor(_Time.y * _Speed);
          float row = floor(time / _HorizontalAmount);
          float column = time - row * _HorizontalAmount;
      
          // -------- 使用行列索引值来构建真正的采样坐标 -------- 
          // 得出帧图像"坐标"------(x: 列索引,y: 行索引)
          // 对垂直方向的坐标偏移使用减法,因为 Unity 纹理坐标正方向与序列帧纹理的正方向相反
          half2 uv = i.uv + half2(column, -row);
          // 除以行/列帧图像数,得到真正的采样坐标
          uv.x /= _HorizontalAmount;
          uv.y /= _VerticalAmount;
      
          fixed4 col = tex2D(_MainTex, uv);
          col.rgb *= _Color;
      
          return col;
      }
    • 设置 Fallback

  4. 将序列帧纹理赋给材质中的 Image Sequence,并将 Horizontal Amount 和 Vertical Amount 设置为正确的值

11.2.2 滚动的背景

步骤:

  1. 去掉天空盒,将摄像机的投影模式改为"正交"

  2. 建材质,建 Shader,将 Shader 赋给材质

  3. 场景中创建四边形(Quad),调整位置与大小,让它充满摄像机的视野范围。将第 2 步的材质赋给它

  4. Shader 代码:(源码

    • 属性声明

      js 复制代码
      Properties
      {
          // 底层(较远)纹理
          _MainTex ("Base Layer", 2D) = "white" {}
          // 表层(较近)纹理
          _DetailTex ("2nd Layer", 2D) = "white" {}
          // 底层纹理滚动速度
          _ScrollX ("Base layer Scroll Speed", Float) = 1.0
          // 表层纹理滚动速度
          _Scroll2X ("2nd layer Scroll Speed", Float) = 1.0
          // 控制纹理整体亮度
          _Multiplier ("Layer Multiplier", Float) = 1.0
      }
    • 顶点着色器

      js 复制代码
      v2f vert (a2v v)
      {
          v2f o;
          o.pos = UnityObjectToClipPos(v.vertex);
      
          // 利用内置的 _Time.y 变量在水平方向上对纹理坐标进行偏移
          // 把两纹理坐标存储在同一变量 o.uv 中,以减少占用的插值寄存器空间
          o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex) + frac(float2(_ScrollX, 0.0) * _Time.y);
          o.uv.zw = TRANSFORM_TEX(v.texcoord, _DetailTex) + frac(float2(_Scroll2X, 0.0) * _Time.y);
      
          return o;
      }
    • 片元着色器

      js 复制代码
      fixed4 frag (v2f i) : SV_Target
      {
          // 分别用 i.uv.xy 和 i.uv.zw 对两张纹理进行采样
          fixed4 firstLayer = tex2D(_MainTex, i.uv.xy);
          fixed4 secondLayer = tex2D(_DetailTex, i.uv.zw);
      
          // 使用第二层纹理的透明通道来混合两张纹理
          fixed4 col = lerp(firstLayer, secondLayer, secondLayer.a);
          // 调整整体亮度
          col.rgb *= _Multiplier;
      
          return col;
      }
  5. 回到编辑器,将底层纹理和表层纹理赋好值,调整好滚动速度即可

效果:

11.3 顶点动画

11.3.1 流动的河流

步骤:

  1. 将摄像机投影类型改为"正交"
  2. 建 3 个材质(因为要模拟 3 层水波),建 Shader,赋给 3 个材质
  3. 创建 3 个四边形(Quad),调整位置、大小和方向,将上步创建的 3 个材质分别赋给它们
  4. Shader 代码:(源码
    • 设置 SubShader 标签

      js 复制代码
      SubShader
      {
          Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Opaque" "DisableBatching"="True" }
      • DisableBatching:包含了模型空间的顶点动画的 Shader,使用 Unity 的批处理功能时会出现问题。这是因为批处理会合并所有相关的模型,令它们各自的模型空间丢失,从而无法进行模型空间的顶点动画。这里就需要取消批处理
    • 设置 Pass 的渲染状态

      js 复制代码
      Pass
      {
          Tags { "LightMode"="ForwardBase" }
          ZWrite Off
          Blend SrcAlpha OneMinusSrcAlpha
          Cull Off
      • 为了让每层水流都能显示

11.3.2 广告牌

Shader 代码:(源码

11.3.3 注意事项

  • 在模型空间下进行顶点动画,往往要关掉批处理,但这样会带来一定的性能下降,增加 Draw Call
    • 应尽量避免使用模型空间下的一些绝对位置和方向来进行计算
    • 广告牌例子中,可利用顶点颜色来存储每个顶点到锚点的距离值
  • 顶点动画的物体,若想正确地往其他物体上投影,需要提供一个自定义的带顶点动画的 ShadowCaster Pass
相关推荐
Thomas游戏开发11 小时前
Unity Android性能优化设置指南
前端框架·unity3d·游戏开发
Thomas游戏开发3 天前
Unity3D C#监听Button点击事件
前端·unity3d·游戏开发
谷宇.6 天前
【Unity3D实例-功能-拔枪】角色拔枪(三)IK的使用-紧握武器
游戏·unity·c#·unity3d·游戏开发·游戏编程·steam
evamango6 天前
《Unity Shader入门精要》十、高级纹理
unity3d
白色牙膏6 天前
从零到搞定:URP Lit彩色箱子材质的开发碎碎念
unity3d
半夜微笑狗8 天前
数据持久化-PlayerPrefs
unity3d
Thomas游戏开发9 天前
博毅创为 Unity_0基础就业班
前端框架·unity3d·游戏开发
谷宇.10 天前
【Unity3D实例-功能-拔枪】角色拔枪(二)分割上身和下身
游戏·unity·c#·游戏程序·unity3d·游戏开发·游戏编程
谷宇.13 天前
【Unity3D实例-功能-移动】角色行走和奔跑的相互切换
游戏·unity·c#·unity3d·游戏开发·游戏编程