Unity shader glsl着色器特效之 模拟海面海浪效果

发布于:2025-02-21 ⋅ 阅读:(13) ⋅ 点赞:(0)

一个简单的海浪效果,通过波的叠加实现水面起伏的动效,根据波峰斜率来为浪花着色,再根据法线贴图和水花贴图来和调整uv的平滑移动来增强海浪移动的细节。如果需要更逼真的效果可以考虑在满足浪花触发的地方添加粒子系统

前置效果图
因为是很久以前写的文章,贴图已经找不到了,随便PS了两张看看效果
请添加图片描述
请添加图片描述
inspector的可调项目
在这里插入图片描述

原理介绍

水波部分使用4个不同参数的Gerstner波叠加,不同波长(10m/5m/3m/7m)不同传播方向(0°/45°/90°/135°),动态计算顶点偏移量:

offset += GerstnerWave(10.0, 0.3, 1.2, 0.0, worldPos, tangent, binormal);

通过交叉计算切线/副切线生成基础法线,叠加噪声贴图增加细节:

float3 noiseNormal = UnpackNormal(tex2D(_NoiseTex, IN.uv_NoiseTex));
o.Normal = normalize(noiseNormal + o.Normal);

水花效果实现,基于表面坡度检测生成水花:

float slope = 1 - o.Normal.y;
float foam = saturate(slope * 5 - 0.7) * foamTex.r;

光源反馈采用菲涅尔反射效果:

float fresnel = pow(1.0 - saturate(dot(o.Normal, IN.viewDir)), 4);

动态镜面反射:

o.Smoothness = _Gloss * (1 - foam);

完整代码

Shader "Custom/OceanWave" {
    Properties {
        _MainColor ("Main Color", Color) = (0.1, 0.3, 0.6, 1)
        _FoamColor ("Foam Color", Color) = (1,1,1,1)
        _WaveScale ("Wave Scale", Range(0,2)) = 0.5
        _WaveSpeed ("Wave Speed", Range(0,5)) = 1.2
        _NoiseTex ("Noise Texture", 2D) = "white" {}
        _FoamTex ("Foam Texture", 2D) = "white" {}
        _Gloss ("Gloss", Range(0,1)) = 0.8
        _NoiseUVSpeed ("Noise UV Speed", Vector) = (0.1, 0.1, 0, 0) // 添加噪声纹理UV移动速度
        _FoamUVSpeed ("Foam UV Speed", Vector) = (0.1, 0.1, 0, 0)  // 添加水花纹理UV移动速度
    }

    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200

        CGPROGRAM
        #pragma surface surf Standard vertex:vert addshadow
        #pragma target 3.0

        #include "UnityCG.cginc"

        struct Input {
            float3 worldPos;
            float3 viewDir;
            float2 uv_NoiseTex;
            float2 uv_FoamTex;
        };

        // 属性声明
        fixed4 _MainColor;
        fixed4 _FoamColor;
        sampler2D _NoiseTex;
        sampler2D _FoamTex;
        float _WaveScale;
        float _WaveSpeed;
        float _Gloss;
        float4 _NoiseUVSpeed;
        float4 _FoamUVSpeed;

        // Gerstner波函数
        float3 GerstnerWave(
            float wavelength,
            float amplitude,
            float speed,
            float direction,
            float3 position,
            inout float3 tangent,
            inout float3 binormal) {
            
            float k = 2 * UNITY_PI / wavelength;
            float c = sqrt(9.8 / k);
            float2 d = float2(sin(direction), cos(direction));
            float f = k * (dot(d, position.xz) - c * speed * _Time.y);
            float a = amplitude / k;

            tangent += float3(
                -d.x * d.x * (amplitude * sin(f)),
                d.x * (amplitude * cos(f)),
                -d.x * d.y * (amplitude * sin(f))
            );

            binormal += float3(
                -d.x * d.y * (amplitude * sin(f)),
                d.y * (amplitude * cos(f)),
                -d.y * d.y * (amplitude * sin(f))
            );

            return float3(
                d.x * (a * cos(f)),
                a * sin(f),
                d.y * (a * cos(f))
            );
        }

        // 顶点着色器
        void vert(inout appdata_full v, out Input o) {
            UNITY_INITIALIZE_OUTPUT(Input, o);
            
            float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
            float3 tangent = float3(1,0,0);
            float3 binormal = float3(0,0,1);
            float3 offset = 0;

            // 叠加四个不同参数的波浪
            offset += GerstnerWave(10.0, 0.3, 1.2, 0.0, worldPos, tangent, binormal);
            offset += GerstnerWave(5.0, 0.2, 1.5, 0.785, worldPos, tangent, binormal);
            offset += GerstnerWave(3.0, 0.1, 2.0, 1.570, worldPos, tangent, binormal);
            offset += GerstnerWave(7.0, 0.15, 1.0, 2.356, worldPos, tangent, binormal);

            // 计算法线
            float3 normal = normalize(cross(binormal, tangent));
            v.normal = mul((float3x3)unity_WorldToObject, normal);
            worldPos += offset;
            v.vertex.xyz = mul(unity_WorldToObject, float4(worldPos, 1)).xyz;
        }

        // 表面着色器
        void surf (Input IN, inout SurfaceOutputStandard o) {
            // 计算UV偏移
            float2 noiseUVOffset = _NoiseUVSpeed.xy * _Time.y;
            float2 foamUVOffset = _FoamUVSpeed.xy * _Time.y;

            // 应用UV偏移
            float2 noisyUV = IN.uv_NoiseTex + noiseUVOffset;
            float2 foamUV = IN.uv_FoamTex + foamUVOffset;

            // 基础颜色
            fixed4 mainColor = _MainColor;

            // 法线计算
            float3 noiseNormal = UnpackNormal(tex2D(_NoiseTex, noisyUV));
            o.Normal = normalize(noiseNormal + o.Normal);

            // 水花效果
            float slope = 1 - o.Normal.y;
            fixed4 foamTex = tex2D(_FoamTex, foamUV);
            float foam = saturate(slope * 5 - 0.7) * foamTex.r;

            // 菲涅尔反射
            float fresnel = pow(1.0 - saturate(dot(o.Normal, IN.viewDir)), 4);
            
            // 最终颜色合成
            o.Albedo = lerp(mainColor.rgb, _FoamColor.rgb, foam);
            o.Metallic = 0.0;
            o.Smoothness = _Gloss * (1 - foam);
            o.Alpha = mainColor.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

网站公告

今日签到

点亮在社区的每一天
去签到