US3S8L10——CSharp代码动态生成程序纹理

C# 代码动态生成程序纹理

本章使用知识点包括:

  1. 利用 Unity 中 Texture2D​ 类生成纹理对象
  2. 利用 Renderer​ 类设置材质球纹理
  3. 利用 Unity 编辑器拓展知识自定义 Inspector 窗口,详见:UEDL7——Inspector窗口拓展
  4. 利用之前实现的单张纹理 Shader 用于测试,详见:US3S2L1——纹理颜色采样

C# 代码动态生成程序纹理相对较简单,我们只需要按需求用代码绘制纹理图片
在需要的时候更新程序纹理即可,更新纹理的时机可以根据需求来定,可以是在编辑模式下,可以是在运行时

修改贴图的某个像素的颜色并应用

设置像素的方法是 texture2D.SetPixel()

  • 参数一:修改的像素所在的 x 轴值
  • 参数二:修改的像素所在的 y 轴值
  • 参数三:像素要修改成的颜色
1
texture.SetPixel(x, y, color);

像素颜色修改后还需要使用 texture2D.Apply()​ 应用修改才能生效

以动态生成国际象棋棋盘格为例:

  1. 设置程序纹理可编辑参数

    纹理宽高、棋盘格行列数、棋盘格两种颜色

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    using UnityEngine;

    public class Lesson88 : MonoBehaviour
    {
    public int textureWidth = 256; // 程序纹理的宽
    public int textureHeight = 256; // 程序纹理的高
    public int tileCount; // 国际象棋棋盘格的行列数
    public Color color1 = Color.white; // 第一种棋盘格的颜色
    public Color color2 = Color.black; // 第二种棋盘格的颜色
    }
  2. 实现更新纹理方法

    1. 创建 Texture2D​ 对象,通过参数设置尺寸

    2. 设置纹理对象每个像素的颜色

      国际象棋棋盘排布规则:国际象棋棋盘的 x 和 y 方向,按格子分,
      格子的行列编号同奇同偶则为白色,不同则为黑色,我们只需要判断 (x, y) 像素所在格子和上面规则的关系即可

    3. 应用像素变化

      使用 tex.Apply()​ 就会应用像素的变化

    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
    /// <summary>
    /// 更新纹理
    /// </summary>
    public void UpdateTexture()
    {
    Texture2D tex = new Texture2D(textureWidth, textureHeight);
    int tileWidth = textureWidth / tileCount;
    int tileHeight = textureHeight / tileCount;
    int tileX = 0;
    int tileY = 0;
    // 遍历生成的纹理的每一个像素点
    for (int y = 0; y < textureHeight; y++)
    {
    for (int x = 0; x < textureWidth; x++)
    {
    // 获取当前像素所在的格子编号,并判断其所属格子编号是否同为奇数或偶数
    tileX = x / tileWidth;
    tileY = y / tileHeight;
    if ((tileX % 2) == (tileY % 2))
    {
    // 同为奇数或偶数,格子为白色
    tex.SetPixel(x, y, color1);
    }
    else
    {
    // 同为奇数或偶数,格子为黑色
    tex.SetPixel(x, y, color2);
    }
    }
    }
    // 应用像素变化
    tex.Apply();
    }

设置材质球主纹理

  1. 获取脚本依附对象的 Renderer​ 渲染器脚本

    Renderer​ 是 MeshRenderer​ 和 LineRenderer​ 的基类

  2. 通过渲染器脚本设置材质球主纹理

    1
    2
    3
    4
    5
    6
    // 得到渲染器,在得到其使用的材质球,对材质球修改其主纹理
    Renderer renderer = this.gameObject.GetComponent<Renderer>();
    if (renderer != null)
    {
    renderer.sharedMaterial.mainTexture = tex;
    }
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
54
55
56
57
58
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Lesson88 : MonoBehaviour
{
public int textureWidth = 256; // 程序纹理的宽
public int textureHeight = 256; // 程序纹理的高
public int tileCount; // 国际象棋棋盘格的行列数
public Color color1 = Color.white; // 第一种棋盘格的颜色
public Color color2 = Color.black; // 第二种棋盘格的颜色

private void Start()
{
UpdateTexture();
}

/// <summary>
/// 更新纹理
/// </summary>
public void UpdateTexture()
{
Texture2D tex = new Texture2D(textureWidth, textureHeight);
int tileWidth = textureWidth / tileCount;
int tileHeight = textureHeight / tileCount;
int tileX = 0;
int tileY = 0;
// 遍历生成的纹理的每一个像素点
for (int y = 0; y < textureHeight; y++)
{
for (int x = 0; x < textureWidth; x++)
{
// 获取当前像素所在的格子编号,并判断其所属格子编号是否同为奇数或偶数
tileX = x / tileWidth;
tileY = y / tileHeight;
if ((tileX % 2) == (tileY % 2))
{
// 同为奇数或偶数,格子为白色
tex.SetPixel(x, y, color1);
}
else
{
// 同为奇数或偶数,格子为黑色
tex.SetPixel(x, y, color2);
}
}
}
// 应用像素变化
tex.Apply();

// 得到渲染器,在得到其使用的材质球,对材质球修改其主纹理
Renderer renderer = this.gameObject.GetComponent<Renderer>();
if (renderer != null)
{
renderer.sharedMaterial.mainTexture = tex;
}
}
}

让一个平面使用之前实现的单张纹理 Shader,并挂载上文实现的脚本,然后运行程序,显示效果如下:

image

在Inspector窗口添加更新纹理按钮

目前的程序纹理只能才程序运行时才能看到效果,而编辑器情况下看不到,因此我们可以:

  1. 为生成纹理脚本新建自定义 Inspector 窗口用脚本
  2. 添加自定义编辑器特性
  3. 重写 OnInspectorGUI()​ 方法,在其中使用 DrawDefaultInspector()​ 方法显示默认组件
  4. 新建按钮,用于调用材质更新方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(Lesson88))]
public class Lesson88_Editor : Editor
{
public override void OnInspectorGUI()
{
DrawDefaultInspector();
Lesson88 scriptObj = target as Lesson88;
if (GUILayout.Button("更新程序纹理"))
{
scriptObj.UpdateTexture();
}
}
}

Inspector 显示内容:

image

点击 "更新程序纹理"​ 后显示内容:

image