Realistic Oceans Using Mesh Deformation!

Is there a way to run :ConnectRenderStepped() on variable frequency (1-60hz for example), depending on how far the character is? It lowers FPS a lot in my game.

You can go into the module’s code and go to the ConnectRenderStepped function. You can copy that function, change the name, and use whatever method you like to update the wave. In that function, I used RenderStepped. You can create a new method. If you want, you can add methods for other render events like Stepped and Heartbeat.

You can use a culling technique, where you put a group of bones in a table, and check it one of the bone’s position is in the camera view, and run that group. On average this reduces 30%-100% (usually 60%) of the amount of bones to update

1 Like

That is actually a really simple idea but I never thought of it. I’ll implement it.

Hello! First of all, thanks a million for the resource! I’ve been looking everywhere for how to do this! I think it’s super cool that roblox has developed technology that even allows for skinned mesh oceans, so I’m super excited to see how far you can go.

I’m in the works of making a Pirate game. I want to use a skinned mesh ocean for it. The map is far too big to place them in manually (About 30k x 28k studs) I’m pretty sure that many bones would crash even Nasa computers haha. I’m really not too great at scripting, so I’m wondering,

How would I be able to modify the system to detect planes anywhere in the workspace? I want to change it to generate infinitely.* In the demonstration, every plane is connected manually. It works fine, but when I try to run a loop to detect new instances or use a function to do the same, I get errors. The big one seems to be that in the module that controls the waves, there’s a loop that collects information from each of the planes.
Any help would be massively appreciated! :smile:

Well you could weld the ocean to the character, only problem with that is the PBR texture will follow the player too, giving away the affect. I think what you would be looking for is manually placed waves, and you may want to do some modifying of my code for optimizations. For example, you can hide waves that is a certain distance away from you and use the MaxRenderDistance to make the waves stop moving when a certain distance away. Also, for performance, laying out the ocean (although it may seem tedious) is the best way to accomplish what you are trying. You could alternatively clone the waves as you go, but would cause lag as cloning is not efficient. Having all waves placed when the player joins is the best solution, and hiding them when a certain distance away is also good. To hide the fact you are making the waves invisible, you can use atmosphere affects to limit the player’s render distance with a seemless edge.

The sheer amount of planes that would need to be placed might be challenging, but I shall take your advice and give something a shot! Thank you very much. :grin:

1 Like

You could use a script to place them automatically at the beginning of the game. Although that will prove challenging for someone who’s strength isn’t scripting. Also it will make building in studio awkward without oceans, you could make a script and use it in your command pad to place it for you.

1 Like

That is something I thought about. However, this doesn’t seem to be efficient for many bones. If I have 4000 bones, that means checking every 4000 times every frame if a bone is visible.

Is there are more efficient method?

You can add an if statement for the :Update method to check whether the wave is visible or not. If it is, move like a wave. This is to make it so instead of hiding individual bones, it hides wave planes instead. This will mean more bones are present but also means that instead of looping over 4000 bones every frame, you only loop maybe 20 times.

1 Like

like I said, bones would be grouped in to multiple tables, so you could have 50+ bones per table and only check one of them (preferably center). Make a second camera with a FOV of ~100 (or more), and have that camera be checking if a group of bones is in view or not. The impact of this for me is negligible, and is significantly compensated with the performance gains.

example:

local BoneGroups = {
   {bone1, bone2, etc.};
   {bone1, bone2, etc.};
   {bone1, bone2, etc.};
   {bone1, bone2, etc.};
};
local AcceptedBoneGroups = {1, 2, 3, 4};

for i, V in pairs(AcceptedBoneGroups) do 
    for _, P in pairs(BoneGroups[V]) do
        -- transform bones
    end
end

you would refresh the AcceptedBoneGroups table before transforming the bones.

5 Likes

Must admit, this is a fantastic release.

Just one thing - As multiple Ocean parts don’t render unless required to do so; they’re sometimes unmatched from the previous wave segment, create a somewhat gap? Is there any advice on how I can fix this?

Sorry for a late response, I somehow missed this. What do you mean by this? Do you mean the line between each waves or the fact that wave planes abruptly stop when far enough away making it look weird when next to active wave planes?

If you meant it looking weird when besides active waves, then a good solution would be to delete the system and edit the module. You can multiply each bone’s wave height by a number signifying how far the bone is by the camera. For example: (in pseudo code)

local dropPerStud = 0.01 — how much the wave height should drop each stud

local newBoneHeight = oldBoneHeight - (distFromCamera * dropPerStud * Math.sign(oldBoneHeight)) 

This would make it so the farmer bones are form the camera, the smaller the
wave actually is. This is useful to make seemless transitions between dead (inactive) waves and real ones. This would make it so the waves far away transition well into dead waves.

To tell if a wave is dead or not, check if the new wave height passes 0. Here is pseudo code:

— Old code
local dropPerStud = 0.01 — how much the wave height should drop each stud

local newBoneHeight = oldBoneHeight - (distFromCamera * dropPerStud * Math.sign(oldBoneHeight)) 

— New Code
local isDeadWave = not (Math.sign(newBoneHeight) == Math.sign(oldBoneHeight))

This would check if the old bone height is on the same side of 0 as the new one.

lol i speak Chinese so i can translate: goodbye! have a good day

1 Like

Regarding your suggestion for laying out the ocean, the lag would be insane (assuming it doesn’t crash). I tried dynamically loading/unloading the ocean plane but due to the # of bones it has a significant lag spike.

Have you tried changing the CFrame to a very far distance and then move it back instead of say, moving it from ReplicatedStorage into workspace? You could use something like partcache. You’ve probably already thought of this, but hey, maybe I helped.

1 Like

Very good idea, that is a good way to prevent Roblox from doing calculations with the bones. An alternative is this:

If this were to be implemented, it would also do what we need it to do.

2 Likes

Yeah. It makes the part “sleep” afaik, which means nothing happens to it and it acts as if it’s anchored

2 Likes

Yeah I don’t think the rendering system touches it.

This is absolutely wonderful, but i’ve been having a few issues with it, it’s probably just me being bad at scripting, but is there a way to have 2 different waves alternate when a event fires, like, in my game have I have a script that fires an event wich triggers a bunch of scripts and starts a storm. when that event is fired, I wanted the waves to become bigger and faster, but I cant seem to get it to work. Thanks in advance