Optimized EditableMesh Ocean Project

So lately I’ve been working on an editablemesh ocean, due to the per-vertex position, uv, normal, and color editing. Overall, editablemesh seems to have more potential for oceans and such than the more trendy (albeit easier to implement) skinned mesh thanks to said per-vertex editing.

Videos:


Regular ocean mesh with uv scrolling:



Underwater:


Regular ocean mesh with uv scrolling + depth-based coloring (this tanks performance by a nice 1-2ms):


The Ocean

I used about 6 gerstner waves to return a height value and applied it over time.
Not planning to use an FFT or any type of Navier-Stokes ocean (although there are a great many papers on them), as it would be too performance-heavy.

Used a PBR for water using high resolution, seamless normal (for wave detail) and albedo map (for surface foam).

Used uv scrolling to add a movement effect to the texture, thanks editablemesh!!! :grin:

Mesh stretches out really far into the horizon and used some fog and DoF effects to make it blend better. Downside of this is that mesh resolution is quite low so I can only resort to using bigger waves. I do plan on fixing this though, to provide high resolution mesh + extending to horizon, which I will expand on later below.

Haven’t tried any client-to-client replication yet, unfortunately. Not a top priority. :man_shrugging:


Performance:

Script is client-sided, usually takes ~2m or less on my laptop with no other applications open.
To gauge this, this was tested on the AMD Ryzen 5 7520U, which is a pretty low-end CPU considering it’s meant for battery-efficient laptops…

I added on option for vertex coloring which usually tanks the performance up to ~2-4ms, but it does provide some satisfying visuals.

Used a ton of optimization methods to do as little math as possible, such as frustum and distance culling, among others.


Future Improvements:

  • Parallelization: high priority, would increase performance significantly, especially if Roblox engineers release a bulk vertex position function

  • High-Res endless mesh: currently one huge mesh, but I plan to create a small one and efficiently duplicate it onto a grid without extra calculations thanks to Editable* becoming Objects

  • Buoyancy: should be easy enough to create and replicate, just need to research the topic a bit more

  • Client-to-Client Replication: if I do end up completely releasing this, it’ll obviously need this as desynchronized waves don’t end up working right with server-handled buoys…

  • Modularization: I’ll probably end up releasing this if people want it or not, but making it customizable through variables will end up being quite difficult (won’t gatekeep, strictly against closed-sourced software).


Tell me what you guys think and thanks a lot if you did end up reading all this yap

:wink:

14 Likes

Wow, that looks awesome! I love how PBR textures make the light reflect off the water!

I can’t wait for editable meshes to release, there is so much great stuff they unlock!

2 Likes

i think underwater lighting and decor needs a lot of work, but overall great stuff

1 Like

This is quite an amazing creation, I think your goals are also pretty good as well. One thing I noticed was that it’s a bit choppy, is there any way you could smooth it out or is this the best it can be?

1 Like

sorry, that’s just my laggy recording :sob:
i’ll try open-sourcing this as soon as I can so you guys can test this out yourselves if you wish.

2 Likes

Don’t make it open source if you don’t want to, it is your creation after all. I’d like to see buoyancy tho, that would be really cool (I haven’t seen this done with custom oceans/bodies of water yet)

no i just plan to open source this as I haven’t seen many good-looking, modularized editablemesh oceans out there. i don’t have much use for it afterwards and I don’t really believe in gatekeeping software.

there have been a few implementations of these with skinned mesh oceans, so it shouldn’t be insanely hard to replicate.

3 Likes

this looks amazing. for client to client replication (i am not 100% sure i understand), couldnt you solve that by using a synced clock like workspace:GetServerTimeNow()? assuming ur talking about syncing the waves themselves. great job either way, if u do end up releasing this i will definitely be using it for a future game!

1 Like

Update


Managed to get the high-res endless mesh off the list.
Thanks to editable* being Objects now, I can replicate one editablemesh across several meshes. This allows me to tile them at lower vertex count but higher visual resolution (5x10 > 10x1).

Also cleaned up some stuff visually ontop of that.


Calmer waves:




More turbulent waves (wasn’t possible before due to low vertex count):



And some underwater caustics gobo that is in progress:


:wink:

5 Likes

Hey this is kind of irrelevant but for some reason when I try to create an EditableMesh and display it, it is invisible? Nothing is displayed at all… How did you pull it off? (Btw I’m using AddVertex and AddTriangles)

i mean it’s somewhat well explained within this thread but you have to use existingMesh:ApplyMesh(editableMeshPart).

my code for this ocean:

local existingWaterMesh = workspace:WaitForChild("water")
local AssetService = game:GetService("AssetService")

local editablemesh = AssetService:CreateEditableMeshAsync(existingWaterMesh.MeshContent, {FixedSize = false})

local meshpart = AssetService:CreateMeshPartAsync(Content.fromObject(editablemesh))

existingWaterMesh:ApplyMesh(meshpart)

existingWaterMesh being the meshpart already in the workspace.
idk it’s a been unintuitive but :man_shrugging:

I see. You are using an existing Mesh. Yeah that worked for me too, but making it from scratch doesn’t? I probably am missing something.

In my water system I’d like to add LOD functionality, which can require decreasing triangles with distance (though I can just use many different existing Meshes now that I think about it LOL)

Thanks a bunch for the help anyways.

yeah had a bit of trouble on that as well but pretty sure you can just do this to create one from scratch:

local AssetService = game:GetService("AssetService")

local editableMesh = AssetService:CreateEditableMesh()

local meshPart = AssetService:CreateMeshPartAsync(Content.fromObject(editableMesh)

would it not tank performance to generate waves for different meshes many times (e.g., 20x20 tiles)? I’ve personally been brainstorming ideas on this in terms of triangle count but to no avail just yet.

but anyways best of luck.

looks nice, only thing that’s missing are caustics on the terrain underwater and godrays from the surface but sadly this may be beyond roblox.

Well, because adding triangles won’t work for some absurd reason, my idea is to use existing MeshPart for the Water.

For the LOD (if you want my advice), simply divide the water into chunks. Why divide it in chunks?
Well, you see, you can make the One MeshPart only so large until you need to place another one, because the more you increase its size, the less detailed and smooth the water is, because there’s the same amount of triangles on more surface.

Contrary to what it seems like, dividing it into chunks with decreasing triangles each time is a great idea. This way less triangles are needed to render an ocean.
By welding everything to the Center Chunk and moving the Center Chunk along with the player (with something like BulkMoveTo or simple CFrames), you get a very performant water system!

yeah i did implement that the only issue is that my smooth brain screwed something up in my code and my gerstner waves aren’t seamless at the tiles.
using modulo makes it look odd at the seams so i need to find where i messed up… :sob:

Godrays are simulated with the Future Lighting Engine (under Lighting), and ONLY at max graphics.

There is a way to make Caustics on Terrain. But it is extremely difficult.

First of all they would have to be pseudo-caustics generated with EditableImage and something like Voronoi Noise.

Then, since we cannot display these on Terrain we would need to turn THE ENTIRE THING into an EditableMesh. This could be done at runtime, once, by raycasting on the Terrain multiple times in a square grid set off by a specific distance, so we could get the Vertex Positions of the MESH from the Terrain.
As you can already see, that’s BUSTED.
Hopefully there’s a way to actually edit the texture of Terrain, but I don’t think so.

Then we would need to Add the generated Color to the Corresponding Terrain Texture.
That gives us… a copy of the Terrain… just for Caustics.
It’s not worth it in the slightest.
It would be worth it if there was a single method to get these Vertices from the Terrain.

Seamless… you mean the chunks have empty spaces right? Yeah that was expected.
Yeah that’s why I originally approached a One Plane tactic. Would be good if roblox wasn’t so broken…

The only way to actually make this work is to go to Blender and individually split the faces… Man that sucks.

no by that i mean they’re not matching up at the seams.
i’ve thought about linear interpolation or something but the thing is i’m doing calculations for only a single tile and then duplicating the applied mesh. so not really an option and would look… eh.

That’s excactly what I meant. And it is NOT your fault. I am tryharding to fix this in Blender rn. There’s a little technique I can do to help with this.