US3S8L11——Shader代码动态生成程序纹理

Shader代码动态生成程序纹理

本章使用知识点包括:

  1. 国际象棋棋盘格规则:格子的行列编号同奇同偶则为白色,不同则为黑色

  2. 数学知识回顾

    1. 两个奇数相加结果为偶数
    2. 两个偶数相加结果为偶数
    3. 一个奇数和一个偶数相加的结果是奇数

向下取整计算

Shader 中的内置函数 floor()​(属于 UnityCG.cginc​),该函数需要传入一个数值 floor(数值)​,会对传入数值向下取整

比如:

  • floor(2.6)​ 返回 2
  • floor(0.4)​ 返回 0
  • floor(-2.3)​ 返回 -3

Shader 代码动态计算国际象棋棋盘格纹理

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

  2. 声明属性

    1. 平铺数量(行列数):_TileCount
    2. 格子颜色1:_Color1
    3. 格子颜色2:_Color2
    1
    2
    3
    4
    5
    6
    Properties
    {
    _TileCount("TileCount", Float) = 8
    _Color1("Color1", Color) = (1, 1, 1, 1)
    _Color1("Color2", Color) = (0, 0, 0, 0)
    }
  3. v2f​ 结构体

    顶点和uv

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    #pragma vertex vert
    #pragma fragment frag

    #include "UnityCG.cginc"

    float _TileCount;
    float4 _Color1;
    float4 _Color2;

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

  4. 顶点着色器

    1. 顶点坐标转换
    2. uv​ 直接赋值
    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;
    }
  5. 片元着色器

    • uv * 行列数​ 将 0 ~ 1 范围 变为 0 ~ _TileCount​ 范围
    • 利用 floor​ 得到行列格子编号
    • 利用奇偶相加规律得到 0、1 值,0 代表同奇或同偶,1 代表不同
    • 利用该值决定该像素使用哪种颜色
    1
    2
    3
    4
    5
    6
    7
    fixed4 frag (v2f i) : SV_Target
    {
    float2 uv = i.uv * _TileCount; // 将uv坐标从 0~1 范围 变为 0~_TileCount 范围
    float2 posIndex = floor(uv); // 得到当前uv坐标所在格子的索引位置
    float value = (posIndex.x + posIndex.y) % 2; // 这里的结果只会是0或1,如果是0即为同偶或同奇,1即为不同
    return lerp(_Color1, _Color2, value); // 因为value只会是0或1,故lerp的值也是在两个颜色里二选一
    }

显示效果:

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
Shader "TeachShader/Lesson89_ShaderPT"
{
Properties
{
_TileCount("TileCount", Float) = 8
_Color1("Color1", Color) = (1, 1, 1, 1)
_Color2("Color2", Color) = (0, 0, 0, 0)
}
SubShader
{
Tags { "RenderType"="Opaque" }

Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag

#include "UnityCG.cginc"

float _TileCount;
float4 _Color1;
float4 _Color2;

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

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

fixed4 frag (v2f i) : SV_Target
{
float2 uv = i.uv * _TileCount; // 将uv坐标从 0~1 范围 变为 0~_TileCount 范围
float2 posIndex = floor(uv); // 得到当前uv坐标所在格子的索引位置
float value = (posIndex.x + posIndex.y) % 2; // 这里的结果只会是0或1,如果是0即为同偶或同奇,1即为不同
return lerp(_Color1, _Color2, value); // 因为value只会是0或1,故lerp的值也是在两个颜色里二选一
}
ENDCG
}
}
}