Making A Mesh Deformation Ocean

Thanks for making this. Ive been trying to figure this out for a while now with no luck. I dont want to use terrain water or a simple part water for my builds, because it either looks too flat or too uncanny. Cant wait to try this out later on.

Do you know how performant it is?

2 Likes

yeah if you can use it right, you can make certain areas have larger waves than others though you’ll need to script it.

1 Like

The performance really depends on how you script it. If your code is really optimized and efficient, it’s better than using terrain water for a big swath of ocean.

Generally, I get spend a lot of time loading and get lag spikes when playing games that use terrain water oceans. With mesh water, though, I scripted a way for the water to be infinite in size and the loading was as though nothing was there. Also had no lag spikes.

I’m by no means a good scripter, so if I can make it work better than terrain water on my machine, I’d say its better.

1 Like

What code did you use to make it preformat?

3 Likes

I haven’t fully got it working yet, but it seems to perform well. Eventually I’m going to have bones closer towards points of interest and farther away the farther out which should help. Figuring out swimming collisions will be fun.

3 Likes

I am busy rn but you could you use perlin wave formula to make it look like a actual wave and some sine formula looking stuff

2 Likes

Actually, a Gernster is what you are going to want to use for this.

2 Likes

Actually, gerstners provide linear motion unlike actual oceans :confused:

2 Likes

But gernsters formula allows for wave crests, unlike a peeling noise formula. Also, Perlin noise is more for land and stuff. So if you are looking to create more realistic waves with actual wave CRESTS, a perlin noise algorithm is not the way to go. :slight_smile:

1 Like

Well if you want crests you could always change your noise density :slight_smile:

1 Like

That wouldn’t make accurate crests, that would just increase the overall height. The whole point of crests are being fo it tie is quickly and crest. Using perlin noise would just make the waves taller, and that’s not what a crest is. :slight_smile:

3 Likes

Thanks, super useful to know! I will definitely be using.

2 Likes

A lot of people need some assistance with the wave part, so here:
I’m not too great with scripting, so any direct scripting advice I give will actually probably be a detriment even if it makes your ocean have individual waves.

I can suggest searching “sine wave” in the roblox library and see what some of those use for their formula. You could get some ideas by tinkering with different scripts like I did.

The general idea is that you want to make each bone have a unique Y value. You do this by making the Y value dependent on the bone’s X and Z values if that makes sense.

You can look at this equation for a simple sine wave.
image

y is the Y value of any point
x is the X value of any point

'a' is the amplitude of the wave. That basically means how high and low the crests and troughs are

392f6207e8f19f0f1da99fcce1d257e0

'b' is the frequency of the wave. That basically means how close each peak and valley is to each other.

'c' is a modifier similar to "time". You change the C value and the wave moves. (its more complicated but that's essentially how you can look at it)

All you do is place any number you want for x,b,c and you get what y should be.
Since you want to make an ocean out of it, you want y to be constantly moving in a consistent and predictable way.

X will be the X value of the bone (and make room for Z, being the Z value of the bone)
A, the amplitude, will be whatever you want (good for weather conditions)
B, the frequency, will be whatever you want (good for weather conditions)
C will be time. tick() or os.time() works, but again they aren’t synchronized between clients, so different players will see the wave at different times which ruins the look of collisions.

You get the Y value and plug that into the bone’s Y value and you have a waving ocean.


Also bear in mind that this is a basic sine wave formula and not a Gerstner wave formula. I haven’t actually done a gerstner wave formula if you can see in my original finished product gif. If anyone can help me with that, that would be great.

10 Likes

Thanks. I have tried some other formula to create my waves but this one is the superior.

1 Like

I considered writing my own version of a guide like this, but instead, I’ll just post a few tips:

  1. If you are using textures rather than a SurfaceAppearance, you can lock the water plane to the position of the camera and make it appear to move properly by scrolling the UV offset of the texture.

  2. Use the same formula that you used to generate the water to detect surface collisions by sampling the wave at the target position rather than trying to detect collisions with the actual wave mesh.

Here is a Gerstner wave function you can use to generate your waves:

--GerstnerWave(Vector3 SamplePosition, Float Wavelength, Vector2 Direction, Float Steepness, Float Gravity, Float SampleTick)
function GerstnerWave(SamplePosition,Wavelength,Direction,Steepness,Gravity,SampleTick)
	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(SamplePosition.X,SamplePosition.Z)) - c * SampleTick
	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
  1. The wavelength of your smallest wave must be larger than the distance between vertices on your water mesh, otherwise, it will look pretty strange.

  2. You can add the displacement from multiple Gerstner waves to get a more convincing final displacement for the water.

29 Likes

OMG IVE BEEN LOOKING FOR SOMETHING LIKE THIS FOR SO LONG!!! Yay

3 Likes

I can’t tell you how much this is amazing timing for me. Due to that being a script imported from blender, would it possible for this to become a model?

4 Likes

Yeah, I could make the ocean mesh a model and link it in the original post.

2 Likes

Hi, I’m having difficulties trying to get this function to work. Could you provide a code snippet, or explain what type each variable has to be?
Thank you.

2 Likes

GerstnerWave(Vector3 SamplePosition, Float Wavelength, Vector2 Direction, Float Steepness, Float Gravity, Float SampleTick)

SamplePosition is the position at which you are checking the wave displacement (E.G. the world position of a given bone in a water mesh)

Wavelength, Direction, Steepness, and Gravity are all pretty self-explanatory, but it does take some tinkering to find numbers that look nice.

SampleTick is the time you are sampling the wave at, should be something that you can sync between clients if possible. Waves sampled at the same SampleTick with the same arguments will always return the same results (deterministic).

Here are some example arguments that I’ve found look pretty nice:

	local Wave1 = GerstnerWave(SamplePosition,80,Vector2.new(1,0),.05,1.5,SampleTick)
	local Wave2 = GerstnerWave(SamplePosition,90,Vector2.new(0,.3),.07,1.5,SampleTick)
	local Wave3 = GerstnerWave(SamplePosition,100,Vector2.new(1,1),.05,1.5,SampleTick)
	local TotalDisplacement = Wave1+Wave2+Wave3 -- This is your final wave displacement
4 Likes