通过改变UV值来移动纹理

在当惊的游戏产业中,一个很常见的游戏纹理技术就是允许你对游戏物体表面的纹理进行滚动。这种技术可以让你创建很多效果,比如瀑布,河流,流动的沿江等等。同时这些技术也是制作动画精灵特效的基础,我们会在这一章节的一系列知识点中来讲解这些内容。 首先,让我们来看看在**表面着色器(Surface Shader)**如何创建一个简单的纹理滚动效果。

  • 始前准备

    在这个知识点开始之前,需要你创建一个新的着色器文件和材质。这么做的目的是为了有个干净的着色器,然后我们可以更加方便的学习和观看滚动效果。

  • 操作步骤

    闲话少说,我们打开刚才创建的的着色器[着色器的名字文章中没有说,就自己取一个启动的名字吧],然后输入下面每个步骤所展示的代码:

    1. 这个着色器需要两个控制纹理滚动的新属性。所以我们添加一个速度属性控制X方向的滚动,添加另一个速度属性控制Y方向的滚动,如下面的代码所示:

       Properties
       {
              _MainTint("Diffuse Tint",Color) = (1,1,1,1)
              _MainTex ("Base (RGB)", 2D) = "white" {}
              _ScrollXSpeed("X Scroll Speed",Range(0,10)) = 2;
              _ScrollYSpeed("Y Scroll Speed",Range(0,10)) = 2;
      
       }
      
    2. 修改CGPROGRAM代码块中的Cg属性中的变量,创建新的变量[把原来的都删掉,用下面展示的代替],这样我们就能访问来自着色器属性的值了:

      fixed4 _MainTint;
      fixed _ScrollXSpeed;
      fixed _ScrollYSpeed;
      sampler2D _MainTex;
      
    3. 修改表面函数surface function从而修改传递给tex2D()函数的UV值。然后,使用内建的_Time变量来对UV进行循环播放的动画,这样的话当我们点击Unity中的运行按钮的时候,我们就能看到动画效果了:

       void surf (Input IN, inout SurfaceOutputStandard o)
       {
         // Create a separate variable to store our UVs
         // before we pass them to the tex2D() function
         fixed2 scrollUV = IN.uv_MainTex;
         // Create variables that store the individual x and y
         // components for the UV's scaled by time
         fixed xScrollValue = _ScrollXSpeed * _Time;
         fixed yScrollValue = _ScrollYSpeed * _Time;
         // Apply the final UV offset
         scrollUV += fixed2(xScrollValue,yScrollValue);
         // Apply textures and tint
         half4 c = tex2D(_MainTex,scrollUV);
         o.Albedo = c.rgb * _MainTint;
         o.Alpha = c.a;
       }
      

    下面的图片中的示例就是利用滚动UV的系统来创建的一个自然环境中河流的动画,你可以注意到场景中叫ScrollingUVs的特效就是来自于本书提供的代码:

  • 原理介绍

    这个纹理滚动的着色器中,定义了一系列的属性,这些属性允许玩家增加或者减少滚动效果的速度。这里的关键点是,来自材质的检查器面板Inspector tab的一些浮点值会输入到着色器的表面函数surface function中去。如果你想了解属性的更多信息,可以去看**第一章,创建你的第一个着色器** 。

    一旦我们获得了来自材质检查器面板Inspector这些浮点值后,我们就可以在着色器中利用它们来对UV值进行偏移修改。

    但是在此之前,我们首先把UV值保存在了一个叫做scrolledUV的独立变量中。这个变量需要是float2/fixed2类型,因为从Input结构传过来给我们的UV值是下面这样的:

    struct Input
    {
    	float2 uv_MainTex;
    }
    

    一旦拿到了游戏网格的UV,我们就可以用我们的滚动速度和着色器内建的**_Time变量偏移修改它们。这个内建的变量会返回float4**类型的变量,也就是说这个变量的每一个部分都包含了不同的时间值,这个时间来自游戏内的时间。

    关于这个独特的时间值,可以通过下面的链接查看它的完整描述:

    https://docs.unity3d.com/Manual/SL-UnityShaderVariables.html

    这个**_Time**变量根据Unity的游戏时钟增量过的浮点值。所以我们能使用这个值,根据UV的两个方向移动我们的UV,并且根据我们的滚动速度对这个时间进行缩放:

    // Create variables that store the individual x and y
    // components for the uv's scaled by time
    fixed xScrollValue = _ScrollXSpeed * _Time;
    fixed yScrollValue = _ScrollYSpeed * _Time;
    

    通过时间算出了正确的UV偏移量,接着就可以添加新的偏移量的值到原来的UV位置中去。这也是我们为什么在下一行要用**+=操作符的原因。我们想要拿到原来的UV位置,对这个位置加上新的偏移量,然后再把这个值传给tex2D()**这个函数作为纹理的新UV。这个过程让我们创建了纹理在游戏对象表面移动的效果。 表面上来看,我们的效果好像在移动纹理,但实际上我们是在修改UV而已:

    scrolledUV += fixed2(xScrollValue, yScrollValue);
    half4 c = tex2D (_MainTex, scrolledUV);