Unity 带光照效果的旗帜Shader

带光照效果的旗帜Shader教程

在Unity中,你可以创建自定义着色器来赋予物体独特的外观和行为。下面是一个用于创建旗帜效果并带有光照效果的着色器示例,让我们逐步了解它的结构和功能。

在这里插入图片描述

Shader 文件结构

Shader "Custom/Flag with lighting"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Ambient ("Ambient", Range(0., 1.)) = 0.2
        [Header(Waves)]
        _WaveSpeed("Speed", float) = 0.0
        _WaveStrength("Strength", Range(0.0, 1.0)) = 0.0
    }
    SubShader
    {
        // ... SubShader 内容 ...
    }
}
Properties
  • _MainTex: 用作主纹理的贴图。
  • _Ambient: 环境光的强度,范围在 0 到 1 之间。
  • _WaveSpeed: 波动速度。
  • _WaveStrength: 波动强度。
SubShader 和 Pass
SubShader
{
    Tags { "RenderType"="Opaque" "LightMode"="ForwardBase" }
    Cull Off

    Pass
    {
        CGPROGRAM
        // ... 着色器内容 ...
        ENDCG
    }
}

着色器主体

结构体定义
struct v2f {
    float4 pos : SV_POSITION;
    float4 vertex : TEXCOORD1;
    float2 uv : TEXCOORD0;
};
计算光照效果
fixed4 _LightColor0;
float _Ambient;

fixed3 diffuseLambert(float3 normal) {
    float diffuse = max(_Ambient, dot(normalize(normal), _WorldSpaceLightPos0.xyz));
    return _LightColor0.rgb * diffuse;
}
顶点变形函数

movement 函数是这个着色器中的一个关键部分,它负责在顶点着色器中对顶点位置进行变形。让我们逐步解释 movement 函数中的逻辑:

float4 movement(float4 pos, float2 uv) {
    float sinOff = (pos.x + pos.y + pos.z) * _WaveStrength;
    float t = _Time.y * _WaveSpeed;
    float fx = uv.x;
    float fy = uv.x * uv.y;
    pos.x += sin(t * 1.45 + sinOff) * fx * 0.5;
    pos.y = sin(t * 3.12 + sinOff) * fx * 0.5 - fy * 0.9;
    pos.z -= sin(t * 2.2 + sinOff) * fx * 0.2;
    return pos;
}

每行代码的解释:

  • float sinOff = (pos.x + pos.y + pos.z) * _WaveStrength;

    • 这行代码根据顶点在 x、y、z 轴上的位置和 _WaveStrength 参数计算一个偏移量。这个偏移量用于产生波动效果。
  • float t = _Time.y * _WaveSpeed;

    • _Time.y 表示自场景启动后的时间,_WaveSpeed 是控制波速的参数。这行代码计算了一个与时间和波速相关的值 t
  • float fx = uv.x;float fy = uv.x * uv.y;

    • 这两行代码根据顶点的纹理坐标 uv 计算出 fxfy,用于在后续计算中对顶点位置进行修改。
  • pos.x += sin(t * 1.45 + sinOff) * fx * 0.5;

    • 根据时间和之前计算的偏移量,以及纹理坐标 fx,在 x 轴方向上对顶点位置进行了一个正弦函数的变换。
  • pos.y = sin(t * 3.12 + sinOff) * fx * 0.5 - fy * 0.9;

    • 同样根据时间、偏移量和纹理坐标 fxfy,在 y 轴方向上对顶点位置进行了正弦函数的变换。
  • pos.z -= sin(t * 2.2 + sinOff) * fx * 0.2;

    • 在 z 轴方向上,根据时间、偏移量和纹理坐标 fx 对顶点位置进行正弦函数的变换。

movement 函数是一个用于在顶点着色器中对顶点位置进行波动和变形的函数。它利用了时间、顶点位置、纹理坐标等参数来产生动态效果,从而在着色器中模拟出旗帜飘动的效果。

顶点着色器
v2f vert(appdata_base v) {
    // ... 顶点着色器逻辑 ...
}
片段着色器
fixed4 frag(v2f i) : SV_Target {
    // ... 片段着色器逻辑 ...
}

代码整体汇总

  • Properties 部分定义了着色器的属性,如贴图、光照参数等。
  • SubShader 和 Pass 定义了着色器的渲染方式和光照模式。
  • 结构体定义v2f)描述了顶点着色器输出到片段着色器的数据结构。
  • 计算光照效果的部分(diffuseLambert)根据法线和光照位置计算漫反射光照。
  • 顶点变形函数movement)对顶点进行波动或变形操作。
  • 顶点着色器vert)用于转换输入顶点并执行其他操作。
  • 片段着色器frag)定义了如何为每个片段(像素)生成最终的颜色输出。
完整shader
Shader "Custom/Flag with lighting"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Ambient ("Ambient", Range(0., 1.)) = 0.2
        [Header(Waves)]
        _WaveSpeed("Speed", float) = 0.0
        _WaveStrength("Strength", Range(0.0, 1.0)) = 0.0
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" "LightMode"="ForwardBase" }
        Cull Off
 
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
 
            struct v2f {
                float4 pos : SV_POSITION;
                float4 vertex : TEXCOORD1;
                float2 uv : TEXCOORD0;
            };
 
            fixed4 _LightColor0;
            float _Ambient;
 
            // Compute the diffuse light
            fixed3 diffuseLambert(float3 normal)
            {
                float diffuse = max(_Ambient, dot(normalize(normal), _WorldSpaceLightPos0.xyz));
                return _LightColor0.rgb * diffuse;
            }
 
            float _WaveStrength;
            float _WaveSpeed;
 
            // Deformation
            float4 movement(float4 pos, float2 uv) {
                float sinOff = (pos.x + pos.y + pos.z) * _WaveStrength;
                float t = _Time.y * _WaveSpeed;
                float fx = uv.x;
                float fy = uv.x * uv.y;
                pos.x += sin(t * 1.45 + sinOff) * fx * 0.5;
                pos.y = sin(t * 3.12 + sinOff) * fx * 0.5 - fy * 0.9;
                pos.z -= sin(t * 2.2 + sinOff) * fx * 0.2;
                return pos;
            }
 
            v2f vert(appdata_base v) {
                v2f o;
                o.vertex = v.vertex;
                o.pos = mul(UNITY_MATRIX_MVP, movement(v.vertex, v.texcoord));
                o.uv = v.texcoord;
                return o;
            }
 
            sampler2D _MainTex;
 
            fixed4 frag(v2f i) : SV_Target {
                fixed4 col = tex2D(_MainTex, i.uv);
 
                // Compute the new normal;
                float3 pos0 = movement(float4(i.vertex.x, i.vertex.y, i.vertex.z, i.vertex.w), i.uv).xyz;
                float3 pos1 = movement(float4(i.vertex.x + 0.01, i.vertex.y, i.vertex.z, i.vertex.w), i.uv).xyz;
                float3 pos2 = movement(float4(i.vertex.x, i.vertex.y, i.vertex.z + 00.1, i.vertex.w), i.uv).xyz;
 
                // Normal in model space
                float3 normal = cross(normalize(pos2 - pos0), normalize(pos1 - pos0));
 
                // Normal in world space
                float3 worldNormal = mul(normal, (float3x3) unity_WorldToObject);
 
                col.rgb *= diffuseLambert(worldNormal);
                return col;
            }
 
            ENDCG
        }
    }
}
  • 9
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

九十分115

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值