43.Shader代码动态生成程序纹理

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


43.1 知识点

知识点回顾

  1. 国际象棋棋盘格规则

    • 格子的行列编号同奇同偶则为白色,不同则为黑色。
  2. 数学知识回顾

    • 两个奇数相加结果为偶数。
    • 两个偶数相加结果为偶数。
    • 一个奇数和一个偶数相加的结果是奇数。

Shader中的内置函数 floor

  • floor 是 Unity 的 UnityCG.cginc 文件中的内置函数。
  • 功能:对传入数值向下取整。
floor(2.6)  // 返回 2
floor(0.4)  // 返回 0
floor(-2.3) // 返回 -3

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

新建shader,命名为可编程纹理,保留骨架

Shader "Unlit/Lesson43_ShaderProgrammableTexture"
{
    Properties
    {
      
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            ENDCG
        }
    }
}

声明属性,包括平铺数量(行列数)以及黑白两个颜色

Properties
{
    // 棋盘格行列数
    _TileCount("TileCount", Float) = 8
    // 格子颜色1
    _Color1("Color1", Color) = (1,1,1,1)
    // 格子颜色2
    _Color2("Color2", Color) = (0,0,0,1)
}
// 定义棋盘格着色器所需的属性变量
float _TileCount;  // 棋盘格的行列数,控制棋盘格的密度
float4 _Color1;    // 格子的第一种颜色
float4 _Color2;    // 格子的第二种颜色

定义结构v2f,包括用于纹理采样的uv坐标以及裁剪空间位置

struct v2f
{
    float2 uv : TEXCOORD0; // 传递 UV 坐标,用于纹理采样
    float4 vertex : SV_POSITION; // 传递顶点的裁剪空间位置,用于渲染
};

顶点函数转换裁剪坐标,并传递输入的uv到片元

v2f vert(appdata_base appdata_base)
{
    v2f v2f; // 初始化输出结构体 v2f

    // 将模型空间下的顶点位置转换为裁剪空间坐标
    // UnityObjectToClipPos 函数将物体的顶点坐标转换为屏幕坐标系中进行渲染的裁剪坐标
    v2f.vertex = UnityObjectToClipPos(appdata_base.vertex);

    // 将输入的 UV 坐标传递到片元着色器
    // 这里直接使用了模型的 UV 坐标来表示纹理的映射位置
    v2f.uv = appdata_base.texcoord;

    // 返回结构体 v2f,包含了裁剪空间的顶点位置和纹理 UV 坐标
    return v2f;
}

片元函数映射uv到格子索引,通过xy格子索引相加的后的奇偶判断哪个颜色显示

fixed4 frag(v2f v2f) : SV_Target
{
    //把uv坐标从0~1范围 缩放到 0~_TileCount 比如0-8
    float2 uv = v2f.uv * _TileCount;
    
    //进行向下取整 相当于就是得到当前uv坐标所在的格子索引位置
    float2 posIndex = floor(uv);
    
    // posIndex.x + posIndex.y 
    //  情况1:如果它们同奇或者同偶 加起来就是偶数
    //  情况2:如果不同一个奇数,一个偶数,加起来就是奇数
    //  这的结果只会是 0或者1 如果是0 代表满足情况1;如果是1 代表满足情况2
    float value = (posIndex.x + posIndex.y) % 2;
    
    //因为value只会是0或1 ,那么我们完全可以利用lerp进行取值
    //取的就是两端的极限值 只有两种情况
    return lerp(_Color1, _Color2, value);
}

赋值Shader并查看效果



43.2 知识点代码

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

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

public class Lesson43_高级纹理_程序纹理_基础实现_Shader代码动态生成程序纹理 : MonoBehaviour
{
    void Start()
    {
        #region 知识点回顾

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

        //2.数学知识回顾
        //  2-1:两个奇数相加结果为偶数
        //  2-2:两个偶数相加结果为偶数
        //  2-2:一个奇数和一个偶数相加的结果是奇数

        #endregion

        #region Shader中的内置函数floor

        //  Shader中的内置函数floor(属于UnityCG.cginc)
        //  该函数 传入一个数值 floor(数值)
        //  会对传入数值向下取整
        //  比如:
        //  floor(2.6)  返回  2
        //  floor(0.4)  返回  0
        //  floor(-2.3) 返回 -3

        #endregion

        #region 知识点 Shader代码动态计算国际象棋棋盘格纹理

        //1.新建Shader,删除无用代码
        //2.声明属性
        //  平铺数量(行列数) _TileCount
        //  格子颜色1 _Color1
        //  格子颜色2 _Color2
        //3.v2f结构体
        //  顶点和uv
        //4.顶点着色器
        //  顶点坐标转换
        //  uv直接赋值
        //5.片元着色器
        //  uv * 行列数 将0~1范围 变为 0~_TileCount范围
        //  利用floor得到行列格子编号
        //  利用奇偶相加规律得到 0、1 值,0代表同奇或同偶,1代表不同
        //  利用该值决定该像素使用哪种颜色

        #endregion
    }
}

Lesson43_ShaderProgrammableTexture.shader

Shader "Unlit/Lesson43_ShaderProgrammableTexture"
{
    Properties
    {
        //棋盘格行列数
        _TileCount("TileCount", Float) = 8
        //格子颜色1
        _Color1("Color1", Color) = (1,1,1,1)
        //格子颜色2
        _Color2("Color2", Color) = (0,0,0,1)
    }
    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; // 传递 UV 坐标,用于纹理采样
                float4 vertex : SV_POSITION; // 传递顶点的裁剪空间位置,用于渲染
            };

            v2f vert(appdata_base appdata_base)
            {
                v2f v2f; // 初始化输出结构体 v2f

                // 将模型空间下的顶点位置转换为裁剪空间坐标
                // UnityObjectToClipPos 函数将物体的顶点坐标转换为屏幕坐标系中进行渲染的裁剪坐标
                v2f.vertex = UnityObjectToClipPos(appdata_base.vertex);

                // 将输入的 UV 坐标传递到片元着色器
                // 这里直接使用了模型的 UV 坐标来表示纹理的映射位置
                v2f.uv = appdata_base.texcoord;

                // 返回结构体 v2f,包含了裁剪空间的顶点位置和纹理 UV 坐标
                return v2f;
            }

            fixed4 frag(v2f v2f) : SV_Target
            {
                //把uv坐标从0~1范围 缩放到 0~_TileCount 比如0-8
                float2 uv = v2f.uv * _TileCount;
                
                //进行向下取整 相当于就是得到当前uv坐标所在的格子索引位置
                float2 posIndex = floor(uv);
                
                // posIndex.x + posIndex.y 
                //  情况1:如果它们同奇或者同偶 加起来就是偶数
                //  情况2:如果不同一个奇数,一个偶数,加起来就是奇数
                //  这的结果只会是 0或者1 如果是0 代表满足情况1;如果是1 代表满足情况2
                float value = (posIndex.x + posIndex.y) % 2;
                
                //因为value只会是0或1 ,那么我们完全可以利用lerp进行取值
                //取的就是两端的极限值 只有两种情况
                return lerp(_Color1, _Color2, value);
            }
            ENDCG
        }
    }
}


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

×

喜欢就点赞,疼爱就打赏