【Unity】【Shader】写一个初步的水波模拟Gerstner Waves

最近看《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"
}

其他效果有待加强~希望能在实践中帮到大家。有错误地方欢迎指出:)

猜你喜欢

转载自blog.csdn.net/leohijack/article/details/78329543
今日推荐