最近看《GPU Gems》,第一章讲解了自然中水波的模拟:https://developer.nvidia.com/gpugems/GPUGems/gpugems_ch01.html,其中提到了广泛使用(没看之前我也是不懂哈哈)的Gerstner Waves,在正弦波的基础上,这个函数能模拟出水波的尖锐波峰效果
下面是我初步模拟出的结果
数学及物理理论可以自己看网站,这边只需了解一点:水波是由N个单一的波叠加形成的,每个点的振幅都需要做高度叠加。
直接上函数及实现:
计算某一个点的水波的高度
其中,x,y即水平坐标(注意:如果使用物体空间坐标,系统自带的Panel的话,水平方向的轴其实是x跟z,竖直方向是y);t即时间,我们使用自带时间变量_Time.x即即可;
Qi:波的陡度参数,0得到正弦波,取 1 / wi × Ai 则为尖锐的波。
Ai:波的振幅
Di:波方向。对于单向波,方向是固定的。对于圆形波,每个点的方向都需要独立计算。
Di.x ;Di.y 即方向的x分量或y分量
ωi:控制波长的参数
Di.(x,y):方向点乘坐标(x,y)
φi:波的初相。这边控制波的抖动频率。
计算法线,参数意义同上
知道了这些,其实就明白怎么做了。我们取一个Panel,由于要计算光照,使用SurfaceShader比较方便,加入半透明支持
Tags{ "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" }以及
#pragma surface surf Standard vertex:vert alpha:fade
下面的完整的Shader,我使用了3个圆形波的叠加:
Shader "Custom/WaveGPUSurface" { Properties { _Color ("Color", Color) = (1,1,1,1) _MainTex ("Albedo (RGB)", 2D) = "white" {} //_Glossiness ("Smoothness", Range(0,1)) = 0.5 //_Metallic ("Metallic", Range(0,1)) = 0.0 _RefTxu("Ref", 2D) = "white"{} _SunPower("Sun Power", Float) = 1.0 _SunDir("Sun Dir", Vector) = (1,1,1,1) _SunColor("Sun Color", Color) = (1,1,1,1) //(A,W,Q,Steep) _Wave1("Wave1",Vector) = (1,1,0.5,0.1) _Wave2("Wave1",Vector) = (1,1,0.5,0.1) _Wave3("Wave1",Vector) = (1,1,0.5,0.1) _StartX("startX", Float) = 0 _C1("WaveC1", Vector) = (1,1,1,1) _C2("WaveC2", Vector) = (1,1,1,1) _C3("WaveC3", Vector) = (1,1,1,1) } SubShader { Tags{ "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" } LOD 200 CGPROGRAM // Physically based Standard lighting model, and enable shadows on all light types #pragma surface surf Standard vertex:vert alpha:fade // Use shader model 3.0 target, to get nicer looking lighting #pragma target 3.0 sampler2D _MainTex; sampler2D _RefTxu; struct Input { float2 uv_MainTex; float3 normal; float3 worldPos; }; half _Glossiness; half _Metallic; fixed4 _Color; float _SunPower; float4 _SunDir; float4 _SunColor; float4 _Wave1; float4 _Wave2; float4 _Wave3; float _StartX; float4 _C1; float4 _C2; float4 _C3; float4 DisVec(float4 v, fixed i) { if (i == 1) { return normalize(v - _C1); } else if (i == 2) { return normalize(v - _C2); } else if (i == 3) { return normalize(v - _C3); } } float DiDotXY(float4 v, fixed i) { return dot(DisVec(v, i), v); } float4 GerstnerWave(float4 v, float t, out float3 normal) { fixed A = 0;//振幅 fixed W = 1;//角速度 fixed Q = 2;//初相 fixed Step = 3;//陡度控制 float CT1 = cos(_Wave1[W] * DiDotXY(v, 1) + _Wave1[Q] * t); float CT2 = cos(_Wave2[W] * DiDotXY(v, 2) + _Wave2[Q] * t); float CT3 = cos(_Wave3[W] * DiDotXY(v, 3) + _Wave3[Q] * t); float xT = v.x + _Wave1[Step] * _Wave1[A] * DisVec(v, 1).x * CT1 + _Wave2[Step] * _Wave2[A] * DisVec(v, 2).x * CT2 + _Wave3[Step] * _Wave3[A] * DisVec(v, 3).x * CT3; float yT = _Wave1[A] * sin(_Wave1[W] * DiDotXY(v, 1) + _Wave1[Q] * t) + _Wave2[A] * sin(_Wave2[W] * DiDotXY(v, 2) + _Wave2[Q] * t) + _Wave3[A] * sin(_Wave3[W] * DiDotXY(v, 3) + _Wave3[Q] * t); float zT = v.z + _Wave1[Step] * _Wave1[A] * DisVec(v, 1).z * CT1 + _Wave2[Step] * _Wave2[A] * DisVec(v, 2).z * CT2 + _Wave3[Step] * _Wave3[A] * DisVec(v, 3).z * CT3; float4 P = float4(xT, yT, zT, v.w); //法线计算 float DP1 = dot(DisVec(v, 1), P); float DP2 = dot(DisVec(v, 2), P); float DP3 = dot(DisVec(v, 3), P); float C1 = cos(_Wave1[W] * DP1 + _Wave1[Q] * t); float C2 = cos(_Wave2[W] * DP2 + _Wave2[Q] * t); float C3 = cos(_Wave3[W] * DP3 + _Wave3[Q] * t); float nXT = -1 * (DisVec(v, 1).x * _Wave1[W] * _Wave1[A] * C1) - (DisVec(v, 2).x * _Wave2[W] * _Wave2[A] * C2) - (DisVec(v, 3).x * _Wave3[W] * _Wave3[A] * C3); float nYT = 1 - _Wave1[Step] * _Wave1[W] * _Wave1[A] * sin(_Wave1[W] * DP1 + _Wave1[Q] * t) - _Wave2[Step] * _Wave2[W] * _Wave2[A] * sin(_Wave2[W] * DP2 + _Wave2[Q] * t) - _Wave3[Step] * _Wave3[W] * _Wave3[A] * sin(_Wave3[W] * DP3 + _Wave3[Q] * t); float nZT = -1 * (DisVec(v, 1).z * _Wave1[W] * _Wave1[A] * C1) - (DisVec(v, 2).z * _Wave2[W] * _Wave2[A] * C2) - (DisVec(v, 3).z * _Wave3[W] * _Wave3[A] * C3); normal = float3(nXT, nYT, nZT); return P; } float fresnel(float3 V, float3 N) { half NdotL = max(dot(V, N), 0.0); half fresnelBias = 0.4; half fresnelPow = 5.0; fresnelPow = _SunPower; half facing = (1.0 - NdotL); return max(fresnelBias + (1 - fresnelBias) * pow(facing, fresnelPow), 0.0); } float3 computeSunColor(float3 V, float3 N) { float3 HalfVector = normalize(abs(V + (_SunDir))); return _SunColor * pow(abs(dot(HalfVector, N)), _SunPower) * _SunColor.a; } void vert(inout appdata_full v, out Input o) { float3 normal = float3(1,1,1); v.vertex = GerstnerWave(v.vertex, _Time.x, normal); UNITY_INITIALIZE_OUTPUT(Input, o); o.normal = normal; } void surf(Input IN, inout SurfaceOutputStandard o) { fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color; float3 N = IN.normal; o.Albedo = c.rgb; o.Alpha = _Color.a; o.Normal = N; float3 vDir = normalize(_WorldSpaceCameraPos - IN.worldPos); float fr = fresnel(vDir, N); //float3 skyColor = texCUBE(_ReflMap, WorldReflectionVector(IN, o.Normal)).rgb * _ReflecTivity;//* _ReflecTivity; float3 sunColor = computeSunColor(vDir, N); o.Emission = fr * c + sunColor; } ENDCG } FallBack "Diffuse" }
其他效果有待加强~希望能在实践中帮到大家。有错误地方欢迎指出:)