Texture2D SourceState; RWTexture2D TargetState; int2 GridSize; float Dt; float Dx; float PlaneLength; float WaveSpeed; float Damping; float FoundationStiffness; float TimeSeconds; float2 BreathingSettings; float4 ImprintSettings; float4 ObjectPositionRadius[2]; float4 ObjectVelocityPush[2]; [numthreads(8, 8, 1)] void MainCS(uint3 DispatchThreadId : SV_DispatchThreadID) { int2 Pixel = int2(DispatchThreadId.xy); if (Pixel.x >= GridSize.x || Pixel.y >= GridSize.y) { return; } int2 Lo = int2(0, 0); int2 Hi = GridSize - 1; float4 State = SourceState.Load(int3(Pixel, 0)); float Height = State.r; float Velocity = State.g; float Imprint = State.b; float Left = SourceState.Load(int3(max(Pixel + int2(-1, 0), Lo), 0)).r; float Right = SourceState.Load(int3(min(Pixel + int2(1, 0), Hi), 0)).r; float Down = SourceState.Load(int3(max(Pixel + int2(0, -1), Lo), 0)).r; float Up = SourceState.Load(int3(min(Pixel + int2(0, 1), Hi), 0)).r; float Laplacian = (Left + Right + Down + Up - 4.0 * Height) / max(Dx * Dx, 1.0e-8); float2 World = (float2(Pixel) + 0.5) * Dx - PlaneLength * 0.5; float Interaction = 0.0; float Contact = 0.0; for (int ObjectIndex = 0; ObjectIndex < 2; ++ObjectIndex) { float4 PositionRadius = ObjectPositionRadius[ObjectIndex]; if (PositionRadius.w <= 0.0) { continue; } float4 VelocityPush = ObjectVelocityPush[ObjectIndex]; float Radius = PositionRadius.w; float Sigma = max(Radius * (0.58 / 0.43), Dx); float2 Offset = World - PositionRadius.xy; float Weight = exp(-dot(Offset, Offset) / max(2.0 * Sigma * Sigma, 1.0e-8)); float HeightAboveContact = max(PositionRadius.z - Radius, 0.0); float ContactHeight = 1.0 - smoothstep(0.0, max(Radius, Dx), HeightAboveContact); float ImpactSpeed = length(VelocityPush.xy) * 0.25 + max(-VelocityPush.z, 0.0) * 0.5; float Push = max(VelocityPush.w, 0.0) + ImpactSpeed; Interaction -= Push * Weight * ContactHeight; if (Push > 0.0) { Contact = max(Contact, Weight * ContactHeight); } } float Breathing = 0.0; if (BreathingSettings.x != 0.0) { float Swell = sin(World.x * 0.48 - TimeSeconds * 0.22) * cos(World.y * 0.42 + TimeSeconds * 0.16); Breathing = BreathingSettings.y * Swell * sin(TimeSeconds * 0.48); } float NextImprint = Imprint; if (ImprintSettings.x != 0.0) { NextImprint = clamp(Imprint * exp(-Dt / max(ImprintSettings.z, 1.0e-4)) - Contact * ImprintSettings.y * Dt, -ImprintSettings.w, 0.0); } float FoundationTarget = ImprintSettings.x != 0.0 ? Imprint : 0.0; float Acceleration = WaveSpeed * WaveSpeed * Laplacian - FoundationStiffness * (Height - FoundationTarget) + Interaction + Breathing; float NextVelocity = (Velocity + Acceleration * Dt) * exp(-Damping * Dt); float NextHeight = Height + NextVelocity * Dt; TargetState[Pixel] = float4(NextHeight, NextVelocity, NextImprint, 0.0); }