Realistic FFT Ocean

Summary

This is an implementation of an Ocean-Spectra, the Philips spectrum, to be exact. It uses Fast Fourier transforms (FFT) to efficiently and quickly generate waves. It uses the EditableMesh instance to its fullest, vertex’s positions and even normals are changed each frame! The project offers wide customizations to artists, to get any type of sea environment that one may need— and it’s even fully open sourced, so anyone can take a look inside and see how it is made. And to top it all of it also includes water caustics using an EditableImage by using Snell’s law in reverse to create almost physically accurate but still fast caustics.

This project took quite a long time to create and finish, starting all the way in September 2023 and ending in April 2024, but do be aware that there were long breaks in between. There is also a good chance that I continue development on this to implement features such as foam (this has been added!), I’ve already looked around and encountered 2 ways of implementing it: using a Jacobian Matrix, which is what is being used in Sea of Thieves (which was a big inspiration for this project), or the naive method that was used by NVIDIA in GPU Gems 2: Chapter 18 where they simply looked at the height of the water to determine the choppiness. Both implementations have their pros and cons but that will ultimately come down to the one that fits this project best.

And lastly I wanted to mention 2 things:

  1. A big thank you to the Roblox Staff that chose my project to be showcased in the 2023 Year in Review, sadly the showcase that I displayed there was an older version, that was lacking quite a lot.
  2. The default settings of this project are meant to stress test it, so I greatly recommend you change the Fourier size to 64.

Showcase


You can see both the caustics and water here, caustics are almost physically accurate, so they are dependant on the water…

Here’s a newer version that includes texture blending



The caustics are fully linked to the sun, so if the sun is down there will be less light to bounce through the water.



You can fully see the caustics here, which are all derived from the sun and normal of the water. Specifically they use Snell’s law in reverse, so they are very close to being physically accurate while being performant.



I went for a sort of stylized effect here by multiplying the normals by 10.






I forgot to invert the foam values :sweat_smile:
Most of the images are outdated from one another, the most up to date images are the 2 images at night with foam and the one that blends the caustics with a texture.

Want to try it yourself?

Well, fear not, this project is fully open source and available to everyone- do note that EditableMeshes and EditableImages are still not available on live Roblox servers, so you will need to open it in Studio to experience it for yourself. You can find the place here.

Found a mistake or a poorly performing piece of code?

Think you found a mistake or found a way to improve performance? Well, if you did then you can open a pull request to https://github.com/Icy-Monster/Phillips-Ocean. Any and all contributions are welcome and greatly appreciated.

References

Tessendorf, Jerry. (2001). “Simulating Ocean Water.” In “Simulating Nature: Realistic and Interactive Techniques,” SIGGRAPH 2001

Scrawk. (2022). Phillips-Ocean. GitHub. Retrieved from https://github.com/Scrawk/Phillips-Ocean

Finch, M., & Cyan Worlds. (2004). Chapter 1: Effective Water Simulation from Physical Models. In Pharr, M., & Fernando, R. (Eds.), GPU Gems: Programming Techniques, Tips, and Tricks for Real-Time Graphics (pp. 9-29). Addison-Wesley Professional.

Guardado, J., & Sánchez-Crespo, D. (2004). Chapter 2: Rendering Water Caustics. In Pharr, M., & Fernando, R. (Eds.), GPU Gems: Programming Techniques, Tips, and Tricks for Real-Time Graphics (pp. 31-50). Addison-Wesley Professional.

Kryachko, Y., & 1C:Maddox Games. (2005). Chapter 18: Using Vertex Texture Displacement for Realistic Water Rendering. In Pharr, M. (Ed.), GPU Gems 2: Programming Techniques for High-Performance Graphics and General-Purpose Computation (pp. 185-203). Addison-Wesley.

81 Likes

Nice!

Thanks for sharing.

What kinda of performance issues might there be or lag or how big can it be used?

Thanks

Looks killa !

4 Likes

Very cool stuff! I love to see developers utilize new features to make cool things. Can’t wait to see what else you make!

1 Like

What kinda of performance issues might there be

So a lot of the performance issues come down to the FFT module, I’ve tried and succeeded in optimizing it before but I’ve also sort of given up on it. The module depends on Complex numbers, which are the main reason it uses so much resources. Each complex number is an array (before optimizations it was a metatable), and as we all know indexing thousands of arrays per frame isn’t exactly a fond thing to do. So if anyone is looking to further optimize it I would definitely recommend trying to switch Complex numbers with Vectors.

Also if you’re having performance problems on the default settings but still want to see it, then I’d recommend you set down the FOURIER_SIZE variable, which is basically a sort of resolution of the ocean, which will decrease the amount of computations that the FFT needs to perform.

Nice! Exquisite! Absolutely dashing! Thank you for your effort with this resource! I’m absolutely shocked!

2 Likes

What a lovely creation! Can’t wait to use it for my next game, escape shark obby! Do you plan on ever adding sea foam to it?

3 Likes

yes, he does (he said that in my dms)

2 Likes

Yes, yes I do. So I’ve been thinking about how to implement the foam for quite a while. I’ve been specifically setting my eyes on 2 methods:

  • The more performant one, but less accurate method: basing it off of height - The way NVIDIA went in GPU Gems 2
  • The less performant one, but more accurate: using a Jacobian determinate

Eventually thought I decided to go with the way that NVIDIA went, basing it off of height. I mainly chose this since if you look at a Jacobian graph (image below) it closely resembles the height, and if we just use the height, something we already have available without extra calculations, then we’ll be able to implement it with minimal performance loss.

image
(Tessendorf 2001)

And here’s the finished product: (I forgot to invert the values :sweat_smile: )

6 Likes

I’ve come back to this project, …for like the third time. A few changes were made and more may come. Most of the work is going to be on optimizing some of the math and caustics, as you can see I’ve actually already done something with the caustics. I’ve made them blend into the texture of the seabed (Texture size is still limited to the Mesh resolution).

Although this is not done without issues, if you look closely some of the rocks don’t tend to behave how they should be. This is because they’re in a different UV position on the texture. The way to fix this would be to get the UV of that specific vertex and change the texture there. Which isn’t currently possible, as the only transformation between UV and World space is by getting the UV space of a Vertex. But since each vertex is not a single pixel on the texture, that won’t work. Meaning, that this is best used on a UV map that is a top down view of the mesh.

For the near future, I’m planning to make the caustics take in account shadows. Let’s also not forget that we’ve still got an entire underwater to make, this is only the start. In the upcoming days and weeks I’ll mostly be focusing on the underwater parts of this, so detecting if you’re underwater, caustics accounting for shadows, and an underwater effect. Hopefully by the next post I make all or at least some of these things will have been already finished.

6 Likes

it looks great but why did the game in both studio and roblox broke tho?

1 Like

So, it seems that a recent major update to both the Editable Mesh and Image has broken everything, not surprised since it was (and still is) a beta feature.

I’m going to be gradually fixing it, hopefully you’ll also see improvements in performance, or at least I’m hoping so. Looking at the new API, it seems that it’s a lot more verbose. I’ve already got the movement and color of the vertices back in a working state and I’ll be gradually fixing the other features.

:smiling_face_with_tear:

2 Likes

Everything is now in a working state!

This whole new API change felt really rushed and unstable, the previous version was much more reliable and better to work with. I’ve had to hard code certain things such as the image resolution, because they’ve decided to remove the Resize() function and not create an alternative (yet). I’ve also decided to hard code stuff like the VertexID, NormalID and ColorID, so that may break in the future, so I’ve already prepared a non hard coded version. Honestly I slightly hope that there’s a new API update, not because I’m some sort of masochist who wants to rewrite it again, but because this new API is utterly garbage. It’s unnecessarily complex and verbose, while missing past features and being more unstable than before. While containing even more bugs than the past version, obscure bugs such as the mesh not rendering if all the vertices are on the same Y level, texture content not changing if you use CreateEditableImageAsync instead of CreateEditableImage, color values went from 0-1 to 0-255 for some reason.

I’m unsure of the performance improvements, mainly because I’m using a VM, but from the performance I’ve seen it seems to be better than before.

4 Likes

its not working any more do you know why

1 Like

I just tried it and it seems to be working fine for me. Although, I did also fix an issue with WaitForChild returning an infinite yield, so if that was the issue it’s fixed now.

If this didn’t fix it then could you copy the errors from your output and any steps to reproduce it?

its still not working do you know why

Thank you, been wanting to test it out. I was talking to DevVexus about it and if we do a bunch of optimization(Lerping, etc) methods we could get it at a stable rate mabye even for a game.

1 Like

I believe this is your issue, accounting the fact that IcyMonstrosity doesn’t have the issue and you do.

If it is, I haven’t found a solution.

I’m not aware of any ways to give you permission to use the image, no clear way to do it through the development items menu at least. Best I can do is give y’all the image and then have you import it yourself and modify the floor texture variable.

image
PS: image is 96x96, because Roblox cannot implement a full API update and didn’t include a lot of features for EditableImages, including the resize function

How did you get the caustics to cast onto parts on the sea floor? i.e how did you get the caustics texture to appear on top of parts on the floor

The caustics themselves sadly don’t adapt to other parts, they only work on the floor texture. I’ve thought of adding that in, and maybe even casting shadows but the performance costs would just be through the roof.

If you’re curious on how I change the texture then I just simply cache the texture buffer and write the RGBA depending on the dot product of the normal and sun. (with an offset)