Issues with Gernster Waves

First off, I’m basing my code around @Sir_Falstaff’s math. Also, I don’t know wether this should be under #help-and-feedback:scripting-support or #help-and-feedback:code-review. If I’m in the wrong category, please let me know! Also, If you can’t directly help me with the code, please at least refer me to some readable resources for someone who isn’t even in High School yet, lol.

My current code is this:

local function Gerstner(Position: Vector3, Direction, Time, Steepness, Wavelength, Gravity)
	local k = (2 * math.pi) / Wavelength
	local a = Steepness/k
	local d = Direction.Unit
	local c = math.sqrt(Gravity / k)
	
	local f = k * d:Dot(Vector2.new(Position.X,Position.Z)) - c * Time
	local cosF = math.cos(f)

	--Displacement Vectors
	local dX = (d.X * (a * cosF))
	local dY = a * math.sin(f)
	local dZ = ( d.Y * (a * cosF))
	return Vector3.new(dX,dY,dZ)
end

local function StackedGernster(Position: Vector3, waterInstance: table)
	local octaves = waterInstance.WaveOctaves
	
	local wave = Gerstner(Position, waterInstance.WaveData.CurrentDirection, waterInstance.WaveData.Time, waterInstance.WaveSteepness, waterInstance.Wavelength, waterInstance.WaveGravity)
	
	for i = 1, octaves do
			
		local modifier = ((octaves / 2) + i) - octaves
			
		local Wavelength = waterInstance.Wavelength + (modifier * 2)
		local Direction = Vector2.new(1 / i, 1 / i)
		local Steepness = (modifier * 0.1) * (1 / waterInstance.WaveSteepness)
		--print(tostring(Wavelength)..",    "..tostring(Direction)..",    "..tostring(Steepness))
				
		wave = wave + ( Gerstner(Position, Direction, waterInstance.WaveData.Time, Steepness, Wavelength, waterInstance.WaveGravity) / Vector3.new(i * waterInstance.WaveLacunarity, i * waterInstance.WaveLacunarity, i * waterInstance.WaveLacunarity) )
	end
	
	return wave

end

(Sorry if the function parameters are weird, I’m just trying to make a working prototype.

As an explanation, the Gernster function is just the math for Gernster waves provided by @Sir_Falstaff. The Stacked Gernster function tries to “layer” waves on top of one another, creating a bit of randomness. Also, waterInstance is just fancy for the table of properties. Also, this is returning a Vector3, which I then convert into a CFrame, and then I use that to update the bones on a water mesh using Mesh Deformation.

However, this formula, even with the randomness, still generates… unrealistic waves. They look unnatural and very 2D. What I mean by this is that it seems like the same pattern repeated over and over again, and front-on it just looks like a wall of waves coming at you. There’s no real boundary to the waves. It looks like a modified sine wave, repeated indefinitely (which is effectively what it is).

I have a few questions on how I could make this system better.

  1. Is there any way to make the waves “curl” over at the crests, and then begin a new wave? For instance, see this image:
    Wave1
  2. I’m trying to use this on a large-scale ocean project. These waves seem very… one-dimensional. As I’ve said, it just seems like the same wave repeated indefinitely on one axis. Is there any way to give the waves more “3D-ness?” In real life, waves go on on one axis for a short amount of time, and end, and then other waves begin, possibly slightly forward or backward from that wave, but usually not exactly aligned. Basically, this:
    OceanWave
    Compared to this:
    SineWave
  3. Is there generally any way to improve the system? Sometimes the randomness part of the StackedGernster function returns infinite values, NaN, etc.

Thank you if you can help, and if you can’t, I guess it was at least thought-provoking!

4 Likes