之前在哔哩哔哩看到个大佬写的几个非常好看的shader,我一直以为这种效果是用PS搞得图像处理,原来只是程序化生成的玩意

这里科普一下

Unity Shader 是 Unity 引擎中用于控制物体渲染效果的核心工具。它通过 ShaderLab 语言编写,并可嵌套 Cg/HLSL 代码,提供了对材质、光照和纹理的高度控制。

unity shader定义

以下是 Unity Shader 的基本框架及其核心概念。

①—Shader 与材质的关系

Shader 是一段程序,用于定义输入(如颜色、贴图)和输出(渲染结果)。通过将 Shader 与参数结合,生成材质(Material),材质可赋予场景中的物体以实现特定的渲染效果。

②—Unity Shader 的基本框架

Unity Shader 的整体结构如下:

已复制
Shader "ShaderName" {
   Properties {
       // 定义属性
   }
   SubShader {
       // 子着色器
       Pass {
           // 渲染通道
       }
   }
   Fallback "Diffuse" // 回退选项
}
  • Properties:定义材质的可调参数,如颜色、贴图等。
  • SubShader:包含一个或多个 Pass,用于适配不同的显卡。
  • Pass:定义具体的渲染步骤。
  • Fallback:当设备不支持当前 Shader 时,使用的替代方案。

③—属性模块(Properties)

Properties 是 Shader 与材质之间的桥梁,用于定义可调参数。常见属性类型包括:

已复制
Properties {
   _Color ("Main Color", Color) = (1,1,1,1) // 颜色
   _MainTex ("Texture", 2D) = "white" {} // 贴图
   _Shininess ("Shininess", Range(0,1)) = 0.5 // 滑动条
}

这些属性可在材质面板中动态调整,并通过 [属性名] 在代码中访问。

④—子着色器(SubShader)

SubShader 是 Shader 的核心部分,包含多个 Pass,用于定义渲染逻辑。每个 Pass 是一个完整的渲染流程,支持以下功能:

已复制
SubShader {
   Tags { "RenderType"="Opaque" }
   Pass {
       CGPROGRAM
       #pragma vertex vert
       #pragma fragment frag
       ENDCG
   }
}
  • Tags:设置渲染标签,控制渲染顺序。
  • Pass:定义具体的渲染步骤,支持顶点和片元着色器。

Shader 类型

Unity 提供三种主要的 Shader 类型:

  • 表面着色器(Surface Shader):封装了光照模型,适合复杂光照场景。
  • 顶点/片元着色器(Vertex/Fragment Shader):灵活性高,可实现自定义效果。
  • 固定函数着色器(Fixed Function Shader):适用于老旧设备,但功能有限。

以下是shader代码

一, 变色按钮

① 普通效果

已复制
Shader "Unlit/BtnClickWaveShader"
{
   Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _ColorLeft ("ColorLeft", Color) = (0,1,0,1)
        _ColorRight ("ColorRight", Color) = (0,0,1,1)
        _Radius ("Radius", Float) = 1
        _RadiusThickness ("RadiusThickness", Float) = 1
        _WaveAlpha ("WaveAlpha", Range(0, 1)) = 0.5
        [HDR] _WaveColor ("WaveColor", Color) = (1,1,1,1)
        [hideInInspector] _CircleScaleOffset ("CircleScaleOffset", Float) = 1 // Width / Height
        [hideInInspector] _MouseUVPos ("MouseUVPos", Vector) = (0,0,0,0)
    }
    SubShader
    {
        Tags { "RenderType"="Transparent" "Queue"="Transparent"}
        Blend SrcAlpha OneMinusSrcAlpha

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

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

            sampler2D _MainTex;
            float4 _MainTex_ST;
            float4 _ColorLeft;
            float4 _ColorRight;

           
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 shape = tex2D(_MainTex, i.uv);
                fixed4 col = fixed4(lerp(_ColorLeft, _ColorRight, i.uv.x).rgb, shape.a);
                return col;
            }
            ENDCG
        }
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

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

            sampler2D _MainTex;
            float4 _MainTex_ST;
            float4 _ColorLeft;
            float4 _ColorRight;
            float _Radius;
            float _RadiusThickness;
            float _CircleScaleOffset;
            float _WaveAlpha;
            float4 _WaveColor;
            float4 _MouseUVPos;

            fixed4 PixelColor(float2 uv)
            {
                // 将uv原点移到中心
                uv -= _MouseUVPos.xy;
                uv = uv * 2 - 1;
                // 离uv坐标原点的距离,排除屏幕长宽比、图片长宽比的影响
                float2 dv = float2(uv.x, uv.y)* float2(_ScreenParams.x / _ScreenParams.y * _CircleScaleOffset, 1);
                float d = sqrt(pow(dv.x, 2) + pow(dv.y, 2));
                // 用小圆减大圆 得到圆环
                fixed3 c = smoothstep(_Radius-_RadiusThickness, _Radius, d) - smoothstep(_Radius, _Radius+_RadiusThickness, d);

                return fixed4(c * _WaveColor.rgb, saturate(c.g)); // -0.5
            }

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 shape = tex2D(_MainTex, i.uv);
                fixed4 wave = PixelColor(i.uv);
                wave.rgb *= _WaveColor.rgb;
                wave.a *= shape.a * _WaveAlpha;
                return wave;
            }
            ENDCG
        }
    }
}

② 带SDF效果

已复制
Shader "Unlit/BtnClickWaveSDFShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _ColorLeft ("ColorLeft", Color) = (0,1,0,1)
        _ColorRight ("ColorRight", Color) = (0,0,1,1)
        [hideInInspector] _CircleScaleOffset ("CircleScaleOffset", Float) = 1 // Width / Height
        [hideInInspector] _CirclePosOffset ("CirclePosOffset", Vector) = (0,0,0,0)
        _Radius ("Radius", Float) = 1.5
        _RadiusThickness ("RadiusThickness", Float) = 0.2
        [HDR] _WaveColor ("WaveColor", Color) = (1,1,1,1)
    }
    SubShader
    {
        Tags { "RenderType"="Transparent" "Queue"="Transparent"}
        Blend SrcAlpha OneMinusSrcAlpha

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

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

            sampler2D _MainTex;
            float4 _MainTex_ST;
            float4 _ColorLeft;
            float4 _ColorRight;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 shape = tex2D(_MainTex, i.uv);
                fixed4 col = fixed4(lerp(_ColorLeft, _ColorRight, i.uv.x).rgb, shape.a);
                return col;
            }
            ENDCG
        }
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

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

            sampler2D _MainTex;
            float4 _MainTex_ST;
            float _CircleScaleOffset;
            float _Radius;
            float4 _CirclePosOffset;
            float _RadiusThickness;
            float4 _WaveColor;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }

            float sdCircle(float2 p, float r)
            {
                return length(p) - r;
            }
            float opAnnular(float sdf, float r)
            {
                return abs(sdf) - r;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 shape = tex2D(_MainTex, i.uv);
                // 全象限
                i.uv -= _CirclePosOffset.xy;
                i.uv.y = 1.0 - i.uv.y;
                i.uv = i.uv * 2 - 1;
                // 消除屏幕拉伸影响
                float w = _ScreenParams.x;
                float h = _ScreenParams.y;
                half co = w/h * _CircleScaleOffset;
                i.uv = float2(i.uv.x * co, i.uv.y);

                float step = 1.0 / w;
                fixed4 circle = smoothstep(step, -step, opAnnular(sdCircle(i.uv, _Radius) , _RadiusThickness));

                fixed4 col = fixed4(circle.rgb, shape.a);
                col.rgb = col.rgb * _WaveColor.rgb;
                col.a *= col.r;
                col.a = _WaveColor.a * col.a;
                
                return col;
            }
            ENDCG
        }
    }
}

效果图

​ image-20260201233916124

二,圆角窗口

已复制
Shader "Unlit/ImageRoundCorner"
{
    Properties
    {
        [PerRendererData]_MainTex ("Texture", 2D) = "white" {}
        _Color ("Tint", Color) = (1,1,1,1)

        // 四个角的半径
        _Radius ("Corner Radius (LTRB)", Vector) = (0.1, 0.1, 0.1, 0.1)

        // 边框
        _BorderWidth ("Border Width", Float) = 0.0
        _BorderColor ("Border Color", Color) = (1,1,1,1)

        // 全局透明度
        _Alpha ("Alpha", Range(0,1)) = 1.0
    }

    SubShader
    {
        Tags
        {
            "Queue"="Transparent"
            "IgnoreProjector"="True"
            "RenderType"="Transparent"
            "PreviewType"="Plane"
            "CanUseSpriteAtlas"="True"
        }

        Cull Off
        Lighting Off
        ZWrite Off
        Blend SrcAlpha OneMinusSrcAlpha

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float4 color : COLOR;
            };

            struct v2f
            {
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
                float4 color : COLOR;
            };

            sampler2D _MainTex;
            float4 _MainTex_TexelSize;
            float4 _MainTex_ST;
            fixed4 _Color;

            float4 _Radius; // 左上、右上、右下、左下 半径
            float _BorderWidth;
            fixed4 _BorderColor;
            float _Alpha;

            v2f vert(appdata v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                o.color = v.color * _Color;
                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv) * i.color;
                float2 uv = i.uv;
                float width = 1.0;
                float height = 1.0;

                float rL = _Radius.x; // 左上
                float rR = _Radius.y; // 右上
                float rB = _Radius.z; // 右下
                float rBL = _Radius.w; // 左下

                float b = _BorderWidth;

                // 四个角圆角+边框处理
                float2 p = uv;

                // 左下角
                if (p.x < rBL && p.y < rBL)
                {
                    float d = distance(p, float2(rBL, rBL));
                    if (d > rBL) col.a = 0;
                    else if (b > 0 && d > rBL - b) col = _BorderColor;
                }

                // 左上角
                if (p.x < rL && p.y > 1 - rL)
                {
                    float d = distance(p, float2(rL, 1 - rL));
                    if (d > rL) col.a = 0;
                    else if (b > 0 && d > rL - b) col = _BorderColor;
                }

                // 右下角
                if (p.x > 1 - rB && p.y < rB)
                {
                    float d = distance(p, float2(1 - rB, rB));
                    if (d > rB) col.a = 0;
                    else if (b > 0 && d > rB - b) col = _BorderColor;
                }

                // 右上角
                if (p.x > 1 - rR && p.y > 1 - rR)
                {
                    float d = distance(p, float2(1 - rR, 1 - rR));
                    if (d > rR) col.a = 0;
                    else if (b > 0 && d > rR - b) col = _BorderColor;
                }

                // 透明度控制
                col.a *= _Alpha;

                return col;
            }
            ENDCG
        }
    }
}

效果图

​ image-20260201234237217

我去,感觉自己对shader的理解程度不到百分之0.01,实锤了我是菜逼看也看不懂改也改不了只会用,听说这玩意的本质是数学,好像是线性代数还是什么,但是我之前线性代数和高等数学差点就挂了……so,数学这种抽象东西还是给AI来吧,人类一败涂地1f4361f4361f436