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

  1. 42.高级纹理-程序纹理-基础实现-CSharp代码动态生成程序纹理
    1. 42.1 知识点
      1. CSharp代码动态生成程序纹理所用知识点
      2. 生成国际象棋棋盘格纹理对象
        1. 主要步骤
        2. 设置程序纹理可编辑参数,包括纹理宽高、棋盘格行列数、棋盘格两种颜色
        3. 实现更新纹理方法。创建Texture2D对象,通过参数设置尺寸。设置纹理对象每个像素的颜色,国际象棋棋盘的 x和y方向按格子分,格子的行列编号同奇同偶则为白色,不同则为黑色,我们只需要判断(x,y)像素所在格子和上面规则的关系即可。最后应用像素变化。
      3. 设置材质球主纹理
        1. 主要步骤
        2. 复制Shader开发入门中 Lesson25_纹理_单张纹理_纹理颜色采样的Shader Lesson25_Single_Texture_Color_Sampler并改名为Lesson42_Single_Texture_Color_Sampler
        3. 创建材质使用单张纹理,丢给一个平面,同时把我们写的脚本也挂上去
        4. 更新纹理函数中得到平面挂载的渲染器并修改纹理为我们生成的国际象棋棋盘纹理,开始时调用更新纹理函数
        5. 可以看到生成了对应国际象棋棋盘纹理,修改参数可以看到生成的格子数量不同
      4. 在 Inspector 窗口添加更新纹理按钮
        1. 主要步骤
        2. 为生成纹理脚本新建自定义 Inspector 窗口
        3. 重写OnInspectorGUI()方法,在其中使用DrawDefaultInspector方法显示默认组件。新建按钮,用于调用材质更新方法。
        4. 现在可以调整参数后,点击编辑器动态的生成纹理了
      5. 总结
    2. 42.2 知识点代码
      1. Lesson42_高级纹理_程序纹理_基础实现_CSharp代码动态生成程序纹理.cs
      2. Lesson42_Editor.cs
      3. Lesson42_Single_Texture_Color_Sampler.shader

42.高级纹理-程序纹理-基础实现-CSharp代码动态生成程序纹理


42.1 知识点

CSharp代码动态生成程序纹理所用知识点

  • 利用 Unity 中 Texture2D 类生成纹理对象
  • 利用 Renderer 类设置材质球纹理
  • 利用 Unity 编辑器拓展知识自定义 Inspector 窗口
  • 利用之前课程实现的单张纹理 Shader 用于测试

生成国际象棋棋盘格纹理对象

主要步骤

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

    • 纹理宽高
    • 棋盘格行列数
    • 棋盘格两种颜色
  2. 实现更新纹理方法

    • 创建 Texture2D 对象,通过参数设置尺寸
    • 设置纹理对象每个像素的颜色
      • 规则:国际象棋棋盘的 x 和 y 方向按格子分
      • 格子的行列编号同奇同偶则为白色,不同则为黑色
      • 我们只需要判断 (x, y) 像素所在格子和上述规则的关系即可
    • 应用像素变化

设置程序纹理可编辑参数,包括纹理宽高、棋盘格行列数、棋盘格两种颜色

  • 程序纹理的宽高:
public int textureWidth = 256;
public int textureHeight = 256;
  • 国际象棋棋盘格的行列数:
public int tileCount = 8;
  • 棋盘格的两种颜色:
public Color color1 = Color.white;
public Color color2 = Color.black;

实现更新纹理方法。创建Texture2D对象,通过参数设置尺寸。设置纹理对象每个像素的颜色,国际象棋棋盘的 x和y方向按格子分,格子的行列编号同奇同偶则为白色,不同则为黑色,我们只需要判断(x,y)像素所在格子和上面规则的关系即可。最后应用像素变化。

/// <summary>
/// 更新纹理
/// </summary>
public void UpdateTexture()
{
    //更具对应的纹理宽高来new一个2D纹理对象
    Texture2D texture2D = new Texture2D(textureWidth, textureHeight);


    //首先需要知道 格子的宽高是多少
    //textureWidth / tileCount = 格子的宽
    //textureHeight / tileCount = 格子的高
    int tileWidth = textureWidth / tileCount;
    int tileHeight = textureHeight / tileCount;

    //遍历是在遍历纹理的像素
    for (int y = 0; y < textureHeight; y++)
    {
        for (int x = 0; x < textureWidth; x++)
        {
            // 获取当前像素所在的格子编号
            // x / 格子的宽(56)= 当前x所在格子编号
            // y / 格子的高 (56) = 当前y所在格子编号

            int tileX = x / tileWidth;
            int tileY = y / tileHeight;

            //要判断一个数 是偶数还是奇数 直接对2取余 如果是0 则为偶数 如果为1 则为奇数
            //判断 x 和 y 方向 格子索引 是否同奇 或者 同偶
            if (tileX % 2 == tileY % 2)
                texture2D.SetPixel(x, y, color1);
            else
                texture2D.SetPixel(x, y, color2);
        }
    }

    //应用像素的变化
    texture2D.Apply();
}

设置材质球主纹理

主要步骤

  1. 复制 Shader 开发入门中的 Lesson25_纹理_单张纹理_纹理颜色采样 的 Shader
  2. 获取脚本依附对象的 Renderer 渲染器脚本
  3. 通过渲染器脚本设置材质球主纹理

复制Shader开发入门中 Lesson25_纹理_单张纹理_纹理颜色采样的Shader Lesson25_Single_Texture_Color_Sampler并改名为Lesson42_Single_Texture_Color_Sampler

Shader "Unlit/Lesson42_Single_Texture_Color_Sampler"
{
    Properties
    {
        //主纹理
        _MainTex("MainTex", 2D) = ""{}
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            //映射对应纹理属性的图片颜色相关数据
            sampler2D _MainTex;
            //映射对应纹理属性的 缩放 平(偏)移数据
            float4 _MainTex_ST; //xy代表缩放 zw代表平移

            v2f_img vert(appdata_base appdata_base)
            {
                v2f_img v2f_img;
                v2f_img.pos = UnityObjectToClipPos(appdata_base.vertex);

                
                //appdata_base.texcoord.xy //代表uv坐标
                //appdata_base.texcoord.zw //代表一些额外信息 例如深度值
                
                //先缩放 后平移 这个是一个固定的算法 规则如此
                //如果没有进行缩放和平移 那么 这个计算后 值是不会产生变化的
                //因为缩放默认值是1和1 ,平移默认值是0和0
                
                v2f_img.uv = appdata_base.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;

                
                //另一种写法 使用纹理的宏
                // Transforms 2D UV by scale/bias property
                //#define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw)
                
                //TRANSFORM_TEX(appdata_base.texcoord.xy, _MainTex);
                
                return v2f_img;
            }

            fixed4 frag(v2f_img v2f_img) : SV_Target
            {
                //这传入的uv 是经过插值运算后的 就是每一个片元都有自己的一个uv坐标
                //这样才能精准的在贴图当中取出颜色
                fixed4 color = tex2D(_MainTex, v2f_img.uv);

                return color;
            }
            ENDCG
        }
    }
}

创建材质使用单张纹理,丢给一个平面,同时把我们写的脚本也挂上去


更新纹理函数中得到平面挂载的渲染器并修改纹理为我们生成的国际象棋棋盘纹理,开始时调用更新纹理函数

/// <summary>
/// 更新纹理
/// </summary>
public void UpdateTexture()
{
    //更具对应的纹理宽高来new一个2D纹理对象
    Texture2D texture2D = new Texture2D(textureWidth, textureHeight);


    //首先需要知道 格子的宽高是多少
    //textureWidth / tileCount = 格子的宽
    //textureHeight / tileCount = 格子的高
    int tileWidth = textureWidth / tileCount;
    int tileHeight = textureHeight / tileCount;

    //遍历是在遍历纹理的像素
    for (int y = 0; y < textureHeight; y++)
    {
        for (int x = 0; x < textureWidth; x++)
        {
            // 获取当前像素所在的格子编号
            // x / 格子的宽(56)= 当前x所在格子编号
            // y / 格子的高 (56) = 当前y所在格子编号

            int tileX = x / tileWidth;
            int tileY = y / tileHeight;

            //要判断一个数 是偶数还是奇数 直接对2取余 如果是0 则为偶数 如果为1 则为奇数
            //判断 x 和 y 方向 格子索引 是否同奇 或者 同偶
            if (tileX % 2 == tileY % 2)
                texture2D.SetPixel(x, y, color1);
            else
                texture2D.SetPixel(x, y, color2);
        }
    }

    //应用像素的变化
    texture2D.Apply();

    Renderer renderer = this.GetComponent<Renderer>();
    if (renderer != null)
    {
        //得到渲染器组件中的材质球 并且修改它的主纹理
        renderer.sharedMaterial.mainTexture = texture2D;
    }
}


void Start()
{
    UpdateTexture();
}

可以看到生成了对应国际象棋棋盘纹理,修改参数可以看到生成的格子数量不同


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

主要步骤

  1. 为生成纹理脚本新建自定义 Inspector 窗口用脚本
  2. 添加自定义编辑器特性
  3. 重写 OnInspectorGUI() 方法,在其中使用 DrawDefaultInspector() 方法显示默认组件
  4. 新建按钮,用于调用材质更新方法

为生成纹理脚本新建自定义 Inspector 窗口

using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(Lesson42_高级纹理_程序纹理_基础实现_CSharp代码动态生成程序纹理))]
public class Lesson42_Editor : Editor
{
}

重写OnInspectorGUI()方法,在其中使用DrawDefaultInspector方法显示默认组件。新建按钮,用于调用材质更新方法。

public override void OnInspectorGUI()
{
    //绘制默认参数相关的内容
    DrawDefaultInspector();

    //获取目标脚本
    Lesson42_高级纹理_程序纹理_基础实现_CSharp代码动态生成程序纹理 scriptObj =
        (Lesson42_高级纹理_程序纹理_基础实现_CSharp代码动态生成程序纹理)target;

    if (GUILayout.Button("更新程序纹理"))
    {
        scriptObj.UpdateTexture();
    }
}

现在可以调整参数后,点击编辑器动态的生成纹理了


总结

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

42.2 知识点代码

Lesson42_高级纹理_程序纹理_基础实现_CSharp代码动态生成程序纹理.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Lesson42_高级纹理_程序纹理_基础实现_CSharp代码动态生成程序纹理 : MonoBehaviour
{
    //程序纹理的宽高
    public int textureWidth = 256;
    public int textureHeight = 256;

    //国际象棋棋盘格的行列数
    public int tileCount = 8;

    //棋盘格的两种颜色
    public Color color1 = Color.white;
    public Color color2 = Color.black;


    void Start()
    {
        #region 本节课所用知识点

        //1.利用Unity中Texture2D类生成纹理对象
        //2.利用Renderer类设置材质球纹理
        //3.利用Unity编辑器拓展知识自定义Inspector窗口
        //4.利用之前课程实现的单张纹理Shader用于测试

        #endregion

        #region 知识点一 生成国际象棋棋盘格纹理对象

        //1.设置程序纹理可编辑参数
        //  纹理宽高、棋盘格行列数、棋盘格两种颜色.
        //2.实现更新纹理方法
        //  2-1:创建Texture2D对象,通过参数设置尺寸
        //  2-2:设置纹理对象每个像素的颜色
        //       规则:国际象棋棋盘的 x和y方向,按格子分,
        //            格子的行列编号同奇同偶则为白色,不同则为黑色
        //            我们只需要判断(x,y)像素所在格子和上面规则的关系即可
        //  2-3:应用像素变化

        #endregion

        #region 知识点二 设置材质球主纹理

        //复制Shader开发入门中 Lesson25_纹理_单张纹理_纹理颜色采样的Shader Lesson42_Single_Texture_Color_Sampler
        //1.获取脚本依附对象的Renderer渲染器脚本
        //2.通过渲染器脚本设置材质球主纹理

        #endregion

        UpdateTexture();

        #region 知识点三 在Inspector窗口添加更新纹理按钮

        //1.为生成纹理脚本新建自定义Inspector窗口用脚本
        //2.添加自定义编辑器特性
        //3.重写OnInspectorGUI()方法,在其中使用DrawDefaultInspector方法显示默认组件
        //4.新建按钮,用于调用材质更新方法

        #endregion

        #region 总结

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

        #endregion
    }

    /// <summary>
    /// 更新纹理
    /// </summary>
    public void UpdateTexture()
    {
        //更具对应的纹理宽高来new一个2D纹理对象
        Texture2D texture2D = new Texture2D(textureWidth, textureHeight);


        //首先需要知道 格子的宽高是多少
        //textureWidth / tileCount = 格子的宽
        //textureHeight / tileCount = 格子的高
        int tileWidth = textureWidth / tileCount;
        int tileHeight = textureHeight / tileCount;

        //遍历是在遍历纹理的像素
        for (int y = 0; y < textureHeight; y++)
        {
            for (int x = 0; x < textureWidth; x++)
            {
                // 获取当前像素所在的格子编号
                // x / 格子的宽(56)= 当前x所在格子编号
                // y / 格子的高 (56) = 当前y所在格子编号

                int tileX = x / tileWidth;
                int tileY = y / tileHeight;

                //要判断一个数 是偶数还是奇数 直接对2取余 如果是0 则为偶数 如果为1 则为奇数
                //判断 x 和 y 方向 格子索引 是否同奇 或者 同偶
                if (tileX % 2 == tileY % 2)
                    texture2D.SetPixel(x, y, color1);
                else
                    texture2D.SetPixel(x, y, color2);
            }
        }

        //应用像素的变化
        texture2D.Apply();

        Renderer renderer = this.GetComponent<Renderer>();
        if (renderer != null)
        {
            //得到渲染器组件中的材质球 并且修改它的主纹理
            renderer.sharedMaterial.mainTexture = texture2D;
        }
    }
}

Lesson42_Editor.cs

using UnityEditor;
using UnityEngine;


[CustomEditor(typeof(Lesson42_高级纹理_程序纹理_基础实现_CSharp代码动态生成程序纹理))]
public class Lesson42_Editor : Editor
{
    public override void OnInspectorGUI()
    {
        //绘制默认参数相关的内容
        DrawDefaultInspector();

        //获取目标脚本
        Lesson42_高级纹理_程序纹理_基础实现_CSharp代码动态生成程序纹理 scriptObj =
            (Lesson42_高级纹理_程序纹理_基础实现_CSharp代码动态生成程序纹理)target;

        if (GUILayout.Button("更新程序纹理"))
        {
            scriptObj.UpdateTexture();
        }
    }
}

Lesson42_Single_Texture_Color_Sampler.shader

Shader "Unlit/Lesson42_Single_Texture_Color_Sampler"
{
    Properties
    {
        //主纹理
        _MainTex("MainTex", 2D) = ""{}
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            //映射对应纹理属性的图片颜色相关数据
            sampler2D _MainTex;
            //映射对应纹理属性的 缩放 平(偏)移数据
            float4 _MainTex_ST; //xy代表缩放 zw代表平移

            v2f_img vert(appdata_base appdata_base)
            {
                v2f_img v2f_img;
                v2f_img.pos = UnityObjectToClipPos(appdata_base.vertex);

                
                //appdata_base.texcoord.xy //代表uv坐标
                //appdata_base.texcoord.zw //代表一些额外信息 例如深度值
                
                //先缩放 后平移 这个是一个固定的算法 规则如此
                //如果没有进行缩放和平移 那么 这个计算后 值是不会产生变化的
                //因为缩放默认值是1和1 ,平移默认值是0和0
                
                v2f_img.uv = appdata_base.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;

                
                //另一种写法 使用纹理的宏
                // Transforms 2D UV by scale/bias property
                //#define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw)
                
                //TRANSFORM_TEX(appdata_base.texcoord.xy, _MainTex);
                
                return v2f_img;
            }

            fixed4 frag(v2f_img v2f_img) : SV_Target
            {
                //这传入的uv 是经过插值运算后的 就是每一个片元都有自己的一个uv坐标
                //这样才能精准的在贴图当中取出颜色
                fixed4 color = tex2D(_MainTex, v2f_img.uv);

                return color;
            }
            ENDCG
        }
    }
}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏