一個簡單的海浪效果,通過波的疊加實現水面起伏的動效,根據波峰斜率來為浪花著色,再根據法線貼圖和水花貼圖來和調整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 200CGPROGRAM#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"
}