Advanced Boat Simulation PART 1 - Building a buoyancy system using Niagara in UE5!

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey what's up YouTube in today's video I'm going to talk about how I achieved this cool little boat simulation the thing is this project is quite Advanced and there's a lot to talk about it can be broken down into three separate systems there's a driven buoyancy system a scene capture driven foam system and a somewhat complex niagara-driven Splash system and so I thought it'd be best to make three separate videos one for each system thus in today's video I'm going to explain how I built a buoyancy system using Niagara but first some context why would you use Niagara to do this isn't Niagara particle system okay so you probably know that there are a million ways to create a water Shader right gearsner waves fft simulation Some Noise textures into scrolling X and Y free books the list of techniques goes on and on right now having made a neat Warrior Shader is great but that's only the tip of the ISB already and there are many more things to consider light buoyance being able to sample the wave height at any given wall location to compute and apply buoyancy forces and make an object float is most often required and is most often complicated you see all these Warrior Shader techniques I just mentioned are great but they all have one thing in common they all are entirely computed on the GPU to displace vertices and they all likely rely on Textures and data stored on the GPU as well and so the CPU is unaware of that and yet most physics engines are entirely CPU driven so buoyancy forces have to be computed and applied on the CPU based on quote unquote with data that is on the GPU when the solution then well you could do a direct feedback or a blocking task it has many names meaning you could force the GPU to compute and send whatever data to the CPU right here right now like say a website and based on it compute on the CPU abundancy Force the thing is that is extremely slow in a nutshell the CPU and GPU both do their own thing at their own pace and so if the GPU were asked to immediately send data to the CPU it will need to well that what he's doing and get in sync with the CPU to dialogue with it that induces what you may call a pipeline store and that's really bad so one solution is to use an asynchronous readback or non-blocking task again it has many names now if the GPU were asked to send data to the CPU it would do so but at a convenient time and that does not induce a pipelines tool but yes one of the drawback of an unsynchronous feedback is that you do not get what you ask for immediately usually though the delay is around one or two frames so it's more than fine for most use cases so the downside is that it's usually not straightforward to implement and as far as I know in Android engine it pretty much requires C plus plus and there's not a lot of documentation on the subject so be prepared to get your hands dirty however that was true until Niagara came along because thanks to Niagara it's no possible to super easily perform asynchronous feedbacks now it may not be as optimized as say a complete custom solution with buildings plus plus I'm not entirely sure honestly from my test it seems to be definitely performant enough to be a variable option and that is what I'm going to demonstrate today now I'd like to mention that the second way to approach this whole by MC program yes asynchronous readback is one solution widely used in the industry but what if you could just avoid any kind of dialogue between the GPU and the CPU you see for some Mass driven Warrior shaders such as fft or girls now waves it's totally possible to do the exact same math on the CPU and compute the wave height without having to request anything from the GP and that's what UV does in its Warrior plugin girls now we have the rendered based on a set of parameters on the GPU to display such a selected water mesh right and those waves are also computed on the CPU using the same exact parameters to get the wave height at a bunch of discrete locations and compute buoyancy on the CPU that solution however may only work with a purely mathematical water area and it introduces some CPU overhead but you at least do get the Wi-Fi without any kind of delay something techniques such as fft may also be quite tedious enough to set up on the GPU that you may not want to rebuild a CPU solution so as usual some pros some cons alright now before actually getting started with the video a couple of disclaimers the system I'm about to demonstrate relies on Niagara but there's on quite a bit of blueprint and because of that performance isn't great it is definitely usable for minimalist project but it isn't scalable as is however it should be pretty straightforward to Transit the performance heavy grouping part to C plus and that pretty much solves that issue although I consider this system to be quite experimental it certainly seems to do the job fine enough and in my opinion is totally a shippable solution for a minimalist in the game anything with a bigger scope though on it certainly would have to be better tested first so I don't guarantee this to be a bulletproof solution okay moreover this system is by no means a replacement for us buoyancy system which is plenty capable in performance that system is however tied to use water system which is girls near waves based and one huge benefit of the technique I'm about to demonstrate is that it pretty much works with any kind of water Shader so that's kinda sweet alright so in this video I'm first going to demo that buoyancy system I just released on my Patron and explain how to set it up right then I'll do some kind of hybrid between a step-by-step tutorial on a breakdown to actually show you how to build search system sounds good let's get started then [Music] so this project comes bundle with four demo levels they all demonstrate the buoyancy system in a very very similar manner there's a boat you can control and there are some floating objects around you the first level showcases an extremely simple example with a water Shader that is simply vertically offset with a simple sine wave the second level uses gearsner waves generated procedurally the third level makes use of that fft height flipbook method I showcased in a previous video and the first and last one showcases there's no waves baked into three books playbooks authored with a tool I showcased in a previous video as well so let's assume the following scenario I have a very basic quarter Shader that uses Garner waves no this isn't touched on your own girls now waves so I'll skip over that part and I have a very simple boat mesh and I want it to float first step is to create a blueprint and add that boat as a static mesh it's simple Collision must be properly set up here I used a bunch of custom convex shapes to approximate its shape but you could start with a simple box Collision then here I may want the mass in kilogram to be automatically computed by a new engine based on that Collision volume or I can override it it's going to play a critical role in how much points if first to apply right and how effective linear an angular drag are which are the two settings I may want to tweak although there are some form of drag built in this buoyancy system so it's not technically needed but still those are three parameters I may want to tweak to get the desired feel on behavior from the physical dissemination okay then it's probably best to set that mesh Collision profile to physics sector also two small tips one lowering its Center of mass might help make the boat be more stable and less prone to tipping over to creating a physical material that has no friction might help make that boat slide against obstacles and possibly the ground as well further the water level is quite low which might lead to a better user experience now I could enable simulate physics here but it's not a requirement because it will be turned on Anyway by this buoyancy actor component I then need to add it comes with a bunch of important settings but more on that in a second I also need this blueprint to implement this blueprint interface which provides two very simple functions one is used to forward the Primitive to simulate and the second function is used for the list of pontoons so let's create an array and make sure it's plugged in here then turn on these three options and place as many pontoons as I need you usually don't need a lot here I'm going to add 7 but you could try with Just 4 to begin with the more you add though the more expensive the simulation is now let's tweak this buoyancy component the first and most important thing to do here is to set the Niagara system to use to perform asynchronous View bags you see each of these four demo levels have all these buoyancy components configured to make use of a specific Niagara system or one for level 01 or 2402 and so on these Niagara systems are all based on a template provided by this plugin which you can just copy and paste and modify to your likings and it's quite easily done all you need to do is to implement a method to sample the wave height and normal at a given work position here so assuming my water Shader uses girls now waves generated via this custom hlsl node well I can pretty much just copy and paste that entire hlsl code in Niagara and use the same move parameters and that's it but I'll have a better chance at explaining this in Greater details during the breakdown okay and really that's pretty much the only thing you need to change in this emitter now that it is set here I may then hit play depending on the mesh mass and the way you set up pontoons it might not float at all or bones like crazy there's likely quite a bit of fine tuning to perform to get the desired Behavior okay so let's go over the available settings time and choose to enable buoyancy right away upon beginplay offset acts as an overall Z offset for all pontoons so rather than changing the Z component of each pontoon individually I may do that here apply wave normal if zeros and buoyancy is strictly vertical if one it's in the direction of the wave normals which isn't physically correct but that may help take some water flow and add some extra sideways movements which I personally now radius is quite an important setting first let me enable the debug view so each pontoon is essentially SV and the buoyancy Force at that pontoon is computed based on how much of that sphere volume is submerged meaning how much water is displaced by it so this radius obviously increases each pontoon's volume and so increases the buoyancy Force they each can generate okay now it might make that both levitate above water if pontoons are too big right but then you can play with that of the valued fixes snap is an experimental feature to try and guess the best resting location rotation on the water surface when buoyancy is just getting started which is something that always annoys me with buoyancy systems you usually have to place objects in Z quite arbitrarily and let them fall first which isn't great and so this is an attempt at fixing this issue it's not perfect though moving on first coefficient acts like an overall multiplier to the buoyancy force and Max Force is quite straightforward here are the quite important values on these will need to be tweaked depending on the mesh Mass these two settings prevent the mesh from bouncing on the Water by dumping the vertical Force when a pontoon is in water drag is also extremely important there's angular drag the more you're the most Stables that mesh is rotation wise but then it also gets increasingly harder to turn this settings are per pontoon so the more pontoon you add the more angular drag is applied right same for linear drag this acts as a break to mimic how difficult something is to move in the water so for a measure of a given mass and volume and for a given amount of pontoons there are slightly some fiddling around to do with these values to find the best buoyancy Behavior but that's pretty much it having set up this boat I can do the same thing with a plank so again make a blueprint make sure that measures collisions at the buoyancy component select the desired Niagara system and tweak that component settings then implement the buoyancy interface to forward that mesh and the list of pontoons and that's it now each pontoon comes at a somewhat High CPU cost because it's all done in Blueprint but more on performance in a minute thus you likely don't want too many pontoons simulated all at once and so you may want to enable slash disable buoyancy on octaves based on proximity you see these planks and crates are configured to not activate buoyancy upon Style in this Pawn blueprint I then have a volume and when those planks and crates overlap with this volume I just get their buoyancy component to call those enable and disable buoyancy functions right and that volume is large enough so that from this point of view that happens outside this View as though when buoyancy is disabled for a particular actor it will freeze the attacker in place and disable fix okay so it will kinda levitate in air and so when enabling buoyancy again for detector you might want to snap that actor on the water surface right away the very last thing I almost forgot to mention for no I assume the water is at 0 in the inward space but obviously it might not all you have to do is to update this material parameter collection water altitude parameter and blueprint and you should be good to go so let's try 300 move that water plane up by 300 units in Z and switch its working and voila for the demo all in all I think it's quite a simple yet elegant system to use now let's get technical and see how it was built all right for once on my YouTube channel I'm going to go over those Basics with a hopefully simple step-by-step tutorial so first I'm going to create an empty Niagara system add an empty emitter and set it to GPU like so then create an object variable named say blueprint then I may also create a Boolean named say B export cool then create a blueprint actor add a cube maybe tweak its scale like so then turn on simulate physics and add that Niagara system I just treat it as a component on the gameplay gets that Niagara component and update its object parameter named blueprint and that is going to point to this group interactor because I want this blueprint hacker to receive readbacks right I may also want to update its Boolean variable named B export and set it to True right away okay having done that heisen needs is broken to implement that Niagara callback interface which allows me to implement this event okay now let's configure Niagara to actually export data this is quite simple during particle update at this exporter module make sure to set this grouping variable here same for its explode Boolean you may also want to limit the tick rate of this export module like so I chose not to here but you do you now I will admit I haven't investigated these settings that much I have a good feeling it's best if I set this to pair particle here but I also have a good feeling that for just a few particles this is unlikely to play the huge role in performance so meh I was a bit lazy here don't mind me anyway cool now as long as this Boolean is true Niagara will expose this 2 pictures on this route to this blueprint hair particle in that blueprint using this interface event I may then Loop through each particle's data and do something with the data right note that these variables are named position velocity and size but don't pay attention to that just think of this chart as a way to pack seven arbitrary fruits whatever they may be right three floats in that Vector three in that one and one left here seven floats isn't that much so it's a bit of a limitation sadly but thankfully here it's going to be plenty enough now this diagram meter is still empty there are no particles so there is no data to export so just to demonstrate it's indeed walking I'm going to spawn a few particles here and there maybe apply Some Noise forces this isn't important I just want to have particles moving on export each Pascal's position and maybe velocity as well why not in Blueprint then Loop through the exported data and loadable lines tool it is indeed working now if I lower the frame rate you may notice that those deep lines are lagging behind a bit and that's that one or two frames delay you get with the synchronous feedbacks nothing you can do about it that's the price to pay for sampling data on the GPU for chip it's asynchronous now let's get rid of that logic because the goal is to implement a buoyancy system right so ideally I'd have my boat or whatever buoyant object and I'd like to compute the wave height that's what's usually called pontoons so just like I explained during the demo I'm going to make an array of vectors in Blueprint and one as well in that Niagara system then the idea is to spawn as many particles as they are entries in that array which is easily done use a spawn burst module get that array's length and voila and though at this point I probably want to make sure this emitter doesn't Loop now in Blueprint let's update that niagara's array to be a copy of this actor's pontoon array the thing is that Niagara system may already have processed that emitter spawn stack by the time I update this list so assuming this Vector area here is empty by default it lightly spawned no particle at all so I think the best cause of action here is to Simply initialize that Niagara system once having updated its array variable this will process geometer spawn event once again and ensure the correct amount of pastels is spawned cool now assuming I added a number of pontoons in that blueprint array I actually have a matching number of particles in that Niagara system next I want to sample the wave height at the spontoons positions so I'm going to add a custom module in the update stack add a vector array input and use the execution index to get one of its entry again I spawned one particle for each entry in that area right so I can just get the partial index to read the desired index from that array and that array contains each pontoon's location but in local space because this list just describes a list of offsets from these actor's origin right so I need to transform that location from local to World space for this to work though I must ensure the scenario component is actually pounded to this mesh so its transform matches this mesh transform back in Niagara I may want to write this position as a particle parameter than other Sprite renderer override its position two double checks that I do indeed have one particle per pontoon in wall space cool will know all I have to do now is use the spontoons wall positions to samples wave height and this is where you may want to do things your own way depending on the technique you use to create your water Shader so let's start with a basic example here I created a super duper simple water material it's a plain opaque material displaced in Z based on time and wall position to create a simple sine wave and I'm going to take one step further and fix the normals like so I'm going to do the same exact thing in Niagara use the same exact parameters and voila I have the same exact vertical offset and normal now two important things by default cosine and sign in matters all have a parallel of one so it's neither in degrees nor in radians so I'd suggest using a period of two pies instead of one and then using sine and cosine in radians in Niagara and you're good to go now that vertical offset only is the actual wave height if the water plane is at 0 in Z inward space and it's possibly not at zero so when I do expose that wave normal and wave height here I may want to add water altitude to convert that vertical offset to the actual wave height in wall space which can be done in many ways most likely you'll want to create a Niagara parameter collection and have a simple fruit parameter that you can update in Blueprint and Sample in all your Niagara emitters like so or create a material parameter collection that way you can also access that water altitude value in your materials if you ever need to unlink a network parameter collection to it that's the approach I chose to use for my system that way I can just update that material parameter collection in Blueprint and the Niagara one is automatically updated as well but I guess you could also assume this value is exported as an offset an account for the altitude later on in Blueprint and really when it comes to the side of things that's all there is to it the rest is done in groupings before moving on to blueprintzel I just want to show a few other examples because I feel like this part here is the only bit that could really cause any confusion so I want to make sure it's Crystal Clear so let's check the second demo level which is the scars now waves that is a simple water Shader that uses custom matches code to generate waves based on a number of parameters parameters that all come from a majority parameter collection I then have Niagara Palmer correction linked to that material parameter Collision that way I just have to worry updating the metal one and I use that Niagara parameter collection in this Niagara read back system here to get those girls now with parameters right I'm using the same exact hlsl code I generate the same exact waves and so that wall position I get the same exact wave offset and normal the only thing that differs when using hlsl code in a material and in Agra is a way output pins are handled in materials you return the default spin value and you write additional output values in naira you don't return anything and write all output values and that's pretty much it things may also differ ever so slightly when sampling textures and making use of specific interfaces but all in all it's extremely similar and that's great now without an elephant in the room and it's something I already mentioned in my gearsner ways video but just to be sure let's go right one more time hey girls in a wave is a mathematical formula that outputs a NYX YZ offset so you cannot just sample the wave height by taking that offset's Z component because sure it's the wave height but offset in X and Y so not to wave height at the wall position you ask for the solution then is to use a nature achiever approach sample with the first time to get that XYZ offset then sample with the second time this time shifted by the inverse of that first Xyz offset rinse and repeat and within three or four iterations you get the wave height the wall position close enough to the desired wall position hopefully that made sense OK let's check the server level real quick this material samples the height free book to dry the water displacement and you guessed it in Niagara I do the same exact thing so if I have some kind of wall position based logic to build UVS to sample that free book texture when I do the same exact thing in Niagara use the particle's wall position build the same exact logic with the same exact nodes then sample that free book texture and what it matches so any water Shader you can come up with make it based on more position then build the same exact logic in Niagara using the particles wall position and voila you should be able to use this buoyancy system with your own water Shader cool moving on at this point this Niagara system sends that sample wave height and normal to this blueprint per particle so per pontoon in rupins and first order of business is to get that pontoon's position in wall space however I'm going to assume the user a set of pontoons with the mesh already scaled as desired so I actually don't want to take this mesh scale into account during the transform which can be done in many ways here I'm just going to control the scale applied during the transform with an inverse scale first then compare the wave height at this pontoon with its actual positioning Z to get how much that pontoon is above or below the water surface right then Clump that depth based on that pontoon's diameter so let's assume its radius is 50 that'd be 50 times 2 next well most buoyancy systems are actually quite simple and rely on spheres to compute how much buoyancy for us to apply because it's super straightforward to do and this system is no different the buoyancy Force you apply can be simplified to acquire the amount of water displaced by this pontoon sphere which is a third of Pi times the depth squared times 3 times the radius minus the depth that's the spontaneous submerged volume now let's try to add this as an upward Force at this pontoon's location and see what we get foreign implementation may vary with the ID is pretty much always the same to have a buoyancy simulation that is say more stable you have to add some amount of drug both vertical and horizontal as well as angular and sure you could rely on this object linear on Android work settings to do the job but that would apply that drag at all times an ideally especially if you have some kind of high speed boats that bounces against waves and can be airborne at times it'd be nice if that high amount of drag was applied only when the boat is in water and so you likely want to compute this drag yourself directly in the buoyancy system so all in all the secret is to generate lots of buoyancy forces which also apply lots of drag there are many many ways to compute drag though I tried my own custom method but it was a bit finicky to fine tune eventually I ended up just copying Yuri's math simply put the more Vertical Velocity in Warrior the more force it adds in the opposite vertical Direction kinda like a break simple horizontal velocity the greater the speed the more quote-unquote break force it applies angular drag is even simpler it just adds the inverse of the current angular velocity multiplied by some factor and that's it it really isn't anything too complicated there's no trigonometry there's no call to expensive functions the math is really straightforward but sadly it just results in a lot of nodes and this is called for each pontoon for each actor to simulate so this entire code May easily be executed say 100 times per tick if you have a couple of boats on screen right and unfortunately that's when you do pay for the blueprint virtual machine overhead the most for loops with many nodes is the worst case scenario for blueprints and so on my Hardware doing this for the seven pontoons seems to cost around 0.6 2.7 milliseconds on the CPU which is quite a bit now I don't have a beefy gaming CPU I have a third gen thread repair so it's quite dated already but still I hardly expect this solution to be scalable even on Modern Hardware the good news is that converting this to a C plus plus function is super easy and would massively improve performance at one point I also try to compute the buoyancy Force directly in Niagara which would be way cheaper that way I could just keep most of that blueprint logic and just apply the buoyancy first computer in Niagara unfortunately that turned out to be a dead end because it proved to be extremely unstable at lower frame rates I'm still unsure why exactly looks like that one or two frames today you get from that asynchronous feedback causes no issues when just reading the wave height but is problematic when also Computing the buoyancy first to apply anyway yeah that's pretty much it really when it comes to this buoyancy system now the next step is likely to move this logic into a component that you can add to any actor you wish to be buoyant which is exactly what I've done with my system right it has an enabled buoyancy function which first checks if the owner implements a grouping interface needed to get the simulated primitive then enable physics on that primitive and get pontoons sponsor specified Niagara system and attach it to that primitive update and re-initialize that Niagara system like I previously showed then start ticking on tick I may then want to First process that snap onto the warrior surface event let's just compute and apply buoyancy for each Pawn tools there's one important detail upon disabling buoyancy it's highly likely I'd want to destroy that Niagara system this components spawn for its owner right the thing is a component may only be destroyed by the actor owning it so not this component but whatever actor this component is added to right and though the other chance I might not want to destroy it after all if I expect the tactor to be buoyant again in like 2 seconds which is why a reference to that Niagara system is kept around so I can skip spawning one upon enabling buoyancy if the one previously spawn still exists and so my point is upon disabling buoyancy I'll broadcast that event and forwards that Niagara system then it's up to you to choose if you wish to destroy it or keep it around right it likely has a very small memory footprint but shouldn't cause any performance concerned since its post so let's say is that the one constraint you have to be aware of with this component driven system okay last thing I may briefly mention is that snap to water feature it basically uses a grid on decent method to find the best fitting plane given a bunch of points which is also a project available on my Patron and the subject of a previous video on my YouTube channel so the idea is the following I get the first wave height sampled at the spontoons and using the best fitting plane algorithm I find the best location normal to place an eye on the buoyant tractor so that's it's ideally in a somewhat resting state already when buoyances just started I suppose one of the potential solution would be to see those pontoons as vertices and thus build a list of triangles then compute each triangle's normal on average or normal to derive a rotation but then I guess there are millions of solutions to this problem mass is really not much strong suit my Approach seemed to work well enough so I stuck with it and that's how I built a custom buoyancy system using Niagara and blueprints now again I consider it to be quite experimental it certainly seems to perform decently enough to be shippable if you keep the amount of buying tractors at any given time low enough but there's other things to consider for instance I'm still unsure how well its scale is considering I do spawn when Niagara system payable interactor adjust synchronous readback cost seems to be pretty much negligible for such a low amount of particles but does it scare well with many systems around seem so but again I even better tested this system that much I also experimented with a quote-unquote unified system where I had one single Niagara system that handled all pontoons for All actors but then it was really complicated to keep track of which pontoon was attached to which actor on manually handled the locality War transforms for All actors here having one system period since it's attached you can work in local space and super easily do the world space transform so it's much easier to work with but there are many things to consider still turns out building a complete water system is hard who knew voila that's it for today's video stay tuned for episode 2 coming up very soon I'll dive into my scene capture driven form system in the meantime if you want to get your hands on this buoyancy system it's available as a church you reward on my patreon please note that it's just a buoyancy system and doesn't include this finished product this one will be released at a later on it regardless you're more than welcome to join my patreon if you want to support me and see more content like this also please consider leaving a like and subscribing to the channel if you like this video I also want to thank all my patrons for their ongoing support I appreciate you all that's it I'll see you in the next video take care of yourself bye foreign [Music]
Info
Channel: Ghislain Girardot
Views: 31,012
Rating: undefined out of 5
Keywords:
Id: hbrBCOxeLqw
Channel Id: undefined
Length: 30min 25sec (1825 seconds)
Published: Sat Jun 24 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.