US3S9L3——滚动的背景

本章代码关键字

1
frac()        // 一般用于保留小数部分,但对负数使用会得到(1 - 小数部分)

获取小数部分

CG 内置函数 frac(参数)​,一般用于保留数值的小数部分,但是负数时要注意,会得到 (1 - 小数部分)
该函数的内部计算规则为:frac(x) = x - floor(x)​,比如:

  • frac(2.5) = 2.5 - 2 = 0.5
  • frac(3.75) = 3.75 - 3 = 0.75
  • frac(-0.25) = -0.25 - (-1) = 0.75
  • frac(-3.75) = -3.75 - (-4) = 0.25

它的主要作用是可以帮助我们保证 uv 坐标 范围在 0~1 之间,相当于

  • 大于 1 的 uv 值重新从 0 开始向 1 方向取值
  • 小于 0 的 uv 值重新从 1 开始向 0 方向取值

分析利用纹理坐标制作滚动的背景的原理

注意点:滚动的背景使用的美术资源图片,往往是首尾循环相连的

基本原理:不停地利用时间变量对 uv 坐标进行偏移运算,超过 1 的部分从 0 开始采样,小于 0 的部分从 1 开始采样

以下图为例制作滚动背景:

map_1_1

用 Shader 实现滚动的背景

  1. 新建 Shader,删除无用代码

  2. 声明属性,属性映射

    主纹理、U 轴速度、V 轴速度(两个速度的原因是因为图片可能竖直或水平滚动)

    1
    2
    3
    4
    5
    6
    Properties
    {
    _MainTex ("Texture", 2D) = "white" {}
    _ScrollSpeedU("ScrollSpeedU", float) = 0.5
    _ScrollSpeedV("ScrollSpeedV", float) = 0.5
    }
  3. 设置透明 Shader 相关内容

    往往这种序列帧图集图片都会有透明区域,因此需要使用透明混合相关的设置,具体详见:US3S3L5——透明度混合

    修改渲染标签的渲染类型为透明,队列为透明,并忽略投影机
    关闭深度写入,开启混合,需要使用 SrcAlpha OneMinusSrcAlpha​ 混合

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    SubShader
    {
    Tags { "RenderType"="Transparent" "IgnoreProjector"="True" "Queue"="Transparent"}

    Pass
    {
    ZWrite Off
    Blend SrcAlpha OneMinusSrcAlpha

    CGPROGRAM
    //...
    ENDCG
    }
  4. 结构体

    顶点和纹理坐标

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    struct v2f
    {
    float2 uv : TEXCOORD0;
    float4 vertex : SV_POSITION;
    };

    sampler2D _MainTex;
    float4 _MainTex_ST;
    float _ScrollSpeedU;
    float _ScrollSpeedV;
  5. 顶点着色器

    顶点坐标转换,纹理坐标直接赋值

    1
    2
    3
    4
    5
    6
    7
    v2f vert (appdata_base v)
    {
    v2f o;
    o.vertex = UnityObjectToClipPos(v.vertex);
    o.uv = v.texcoord;
    return o;
    }
  6. 片元着色器

    • 利用时间和速度对 uv 坐标进行偏移计算
    • 利用偏移后的 uv 坐标进行采样
    1
    2
    3
    4
    5
    6
    fixed4 frag (v2f i) : SV_Target
    {
    // 利用时间来计算UV的偏移,因为时间一直在变化,因此最终的UV坐标也不停在变,使用frac是确保uv坐标处于0~1范围内
    float2 scrollUV = frac(i.uv + float2(_Time.y * _ScrollSpeedU, _Time.y * _ScrollSpeedV));
    return tex2D(_MainTex, scrollUV);
    }

显示效果:

image

完整 Shader 代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
Shader "TeachShader/Lesson93_ScrollingBackground"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_ScrollSpeedU("ScrollSpeedU", float) = 0.5
_ScrollSpeedV("ScrollSpeedV", float) = 0.5
}

SubShader
{
Tags { "RenderType"="Transparent" "Queue"="Transparent" "IgnoreProjector"="True" }

Pass
{
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha

CGPROGRAM
#pragma vertex vert
#pragma fragment frag

#include "UnityCG.cginc"

struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};

sampler2D _MainTex;
float4 _MainTex_ST;
float _ScrollSpeedU;
float _ScrollSpeedV;

v2f vert (appdata_base v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
return o;
}

fixed4 frag (v2f i) : SV_Target
{
// 利用时间来计算UV的偏移,因为时间一直在变化,因此最终的UV坐标也不停在变,使用frac是确保uv坐标处于0~1范围内
float2 scrollUV = frac(i.uv + float2(_Time.y * _ScrollSpeedU, _Time.y * _ScrollSpeedV));
return tex2D(_MainTex, scrollUV);
}
ENDCG
}
}
}