最近看《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"
}
其他效果有待加强~希望能在实践中帮到大家。有错误地方欢迎指出:)