Unreal Engine 5.3 - Animated Raindrop Ripples In HLSL

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
in this video we'll take a look at how to make a fully procedural raindrop Ripple like effect in unreal using hlsl coding within a custom node if you are new to hlsl or just discovered these videos and find yourself having trouble following along I would suggest you watch the other videos within this playlist which go over the basics of some hlsl coding and unreal the link to this playlist is in the description below to begin we're going to start off fairly simple we'll create a brand new material and start off with creating a custom node on this custom node we're going to want to create some inputs we're going to create an input for UVS so we can at least connect up some texture coordinates once we've done this the next thing that we're going to do is break down the rain drop ripple effect to the most simplest form possible so if we think about it it's really just a bunch of circles or ripples that start small and then animate spanning outwards and these ripples are randomly placed or scattered over the UV space when they're drawn so the very first thing that we might want to start with is how to draw a circle we've done this a couple times before but we're going to start from the very Basics just so we can build up our code so we're not trying to accomplish something that's too complicated without having something to start with so we'll start off doing some of this code in Visual Studio code just so it's a bit easier to see the syntax highlighting so let's start off with the float for called result and that stores our red green blue and Alpha channels so I'm just going to set that to zero and we're just going to initialize that variable the next thing I'm going to do is make a float 2 so that stores two values for the point Center that's going to be the center of where our point or circle gets drawn and in this case I'd probably want that for now just to be the center of my UV space so 0.5 and 0.5 and then what we're going to do is create another float too which will be our UV offset which is pretty much going to be our UV minus our center of our point or Circle that we want to draw and then we're going to Define our radius so make a float called radius and start off with a very small value and then finally we'll kind of draw our Circle by doing a float like a new variable called Point distance and get the length of our UV offset and then to use all this to actually produce our Circle we'll do an if statement to see if that point distance is within our radius that we've defined and if it is it will output the color white to our result so I'll set this to one for everything and then finally we have to return that result to Output it so that kind of ends up being our starting code and we've done this before if I go over here and I I paste this in here assuming there's no errors there we go we have a circle and we can control its radius by changing our radius variable so now all we need to do is find a way to turn the circle into a ring-like shape and kind of hollow out the center to do that we're going to jump back to our code and we're going to modify a couple things we're going to add some additional variables so some variables I'm going to add is going to be to add something to control the radius of that inner circle and the Outer Circle and it fills in the gap between those two radiuses so to do this I'm going to create a new variable so I'll make it a float and I'll call it radius Min that's the minimum radius to inside of the ring-like shape and then I'll make another float called radius Max and this is going to be the outside shape or size so there we go the minimum is 0.05 the outside is 0.1 so it should be kind of 0.5 thick in between those two sizes and then one thing that we might want to do just for an additional control is have the ability to add some extra thickness if we wanted to so I'm going to make one more float which is just going to be called maybe ring thickness and that's just going to be an additional thickness we can add so maybe I can add like a very small amount of additional thickness and that's something that we can just control so it's not required but might be useful so there's my three new variables now we're going to have to change our if statement to include that radius Min and radius Max so it only knows to fill in the areas in between those two radiuses with uh White so we actually see that the output so Point distance is less or equal to radius that's kind of okay but we're gonna flip this around a little bit maybe what we'll do is point distance is less or equal to and instead of actually less or equal to we'll do greater or equal to radius minimum the minimum size of that inner part of the Ring and then we'll minus the ring thickness because that might add some extra thickness so if the point distance is greater or equal to radius Min minus ring thickness and Point distance is less or equal to radius Max Plus ring thickness then fill in as kind of white if it follows those conditions and matches that so if we take this and we put that back into our our code here let's see what we get okay now we have a ring like shape we can control the the inner radius size and then also the outer radius size and we also have a control to add some extra thickness if we want on both sides so now we have our ring like shape now going back to our code we might want to add a little bit of a tape or fall off to that ring right now it just feels very thick it's almost like a donut shape it'd be nice to have a little bit of control over the gradient or tapering of the opacity on parts of that ring particularly the the inside of it just to make it feel like a little bit of a fall off and a bit smoother and softer so we're going to make some changes to do that before I do that I'm just going to reorganize some of this code because this isn't as readable to me as if I do something like that I usually try to keep the code simple as much as I can at the beginning so it doesn't get hard to look at even though it usually does but the next thing that we're going to do is try to get a bit of a taper or fall off or gradient on the inner part of this ring so when I zoom up really close this inside section it'd be nice if there could be a bit of a gradient from the outside section tapering off to the inside section so to do that I'm not gonna I'm not going to color this ring in with uh a one value or just a color white instead I'm going to find a new variable so I'm going to make a float called Alpha and this will be the opacity or kind of what we draw but we're going to do some adjustments to it we're going to use a smooth step to add a bit of a gradient so I'm going to make this float Alpha this new variable equal something like Smooth step as an radius Min so the minimum radius minus the ring thickness and then for the second parameter we're gonna feed our smooth step it'll be radius Min Plus ring thickness so on that inner part of the radius the minimum side the the ring that's on the innermost side it's going to do a smooth step and it's going to use our Point distance here and if we do something like that it should produce a bit of a gradient one thing I'm going to do is I'm also going to wrap the smooth step inside of a saturate and that just guarantees they're kind of clamps it to be within a range of 0 2 to 1. so we don't run into any we don't run to any issues with uh ranges or values being returned that are negative or that are going over one and starting to Glow I'm just going to restructure his code make sure my brackets are good open open close close Okay and then we'll take this pop that back into unreal see what we get no errors okay now I don't see anything yet because one thing I'm not actually using that variable yet so instead of one down here we're gonna place it with Alpha and actually I don't need to replace all these with Alpha I could just do result and instead of equaling float4 alpha alpha alpha all the way through what I'm just going to do is this result plus equals Alpha and that should be fine and if we take that paste it back in here there we go we got a little bit of a gradient on the inner side of our ring now and you could do the same thing with the the outer side of the ring if you wanted to you know it's the same kind of approach but this is kind of what we want for now so now we have this little bit of taper so if we were to modify some of our values like maybe what we can do is if we reduce the ring thickness it's very very subtle if we increase the ring thickness see it a little bit more ring maximum ring minimum you can start changing this and you always get that little paper so now that we have this gradient the next thing that we're going to want to do is probably make this ring a little bit thinner but we're also going to want to have a random position and a random radius having all these Rings be the same size probably not ideal and we want them to spawn at random locations so that's the next thing that we're going to do now before we go too much further we should just clean up our code a little bit because there's some things in here that we no longer use now that we have a radius minimum and a radius maximum this radius uh variable here is no longer really used so we can remove that and just just start to to do a bit of cleanup on here just to make sure there's nothing that we have in here that we're not using now the next thing that we're going to do is have to add a couple more variables so first thing is we want to Generate random position or just some random values and we want to create a loop to place these different raindrop rings on different areas of our UV so we need a variable for how many drops we're going to place we also need a variable for the offset range within what range is it going to choose a random number to offset these pixels by or where is it going to draw these pixels on the UV space and then we'll need like a seed value to generate some numbers that constantly change and we've kind of done this in in some of our our previous videos as well so it's a little bit of reuse of what we've done before but adding a bit more complexity to it and then maybe it'd be good to have a control to adjust that inner fade as well right now we have that smooth step fade for the inner part of the ring but we have no control over it so I'll block something in for that as well so make a float called maybe fade inner just send it to like some low value for now it's not going to have any functionality yet and then we're going to create our seed variable so float2 seed and we'll make that a flow to float too because he's going to hold two numbers X and Y or u and v two values so those two values there's going to be some random numbers so one two three four five six and seven eight nine zero one two it could be whatever you want it doesn't have to be these numbers but just do something with a couple decimal places so uh you get some somewhat feeling of fake randomization in there and it really jumbles up the numbers and then we need one more variable for offset also float too because X and Y or u and v has this place that pixel on a random spot in both directions left and right and up and down so we'll call this float2 offset range make it a float two and its range will be negative one two one and we're going to set up our UV space to to be within the range of negative one to one instead of zero to one uh just to make this a little bit easier to to understand but it doesn't really matter too much but we'll start with that and then finally one more variable for how many droplets or how many of these little circles or rings we're gonna draw so I'll call it float drops and I'll just make it a hundred uh for a starting value so now we have our variables defined now we need to make sure these variables get added in our code that actually affects something and I have functionality so let's start off with creating our Loop so what we're going to do is start our for Loop so it could repeat what we've done here but multiple times to place our Circle or rank in different areas using that seed value for some fake randomization so to do this what we're going to do is we're gonna have to restructure a little bit of our code because this will get a little bit more complex now so I'm going to start off with our for Loop so four I'm going to start it here four and I it's the integer equals zero I is less than the number of drops that we've defined which is 100 then I plus plus and then it runs everything within the the brackets and we're going to put all this stuff within those brackets so like that and now we have to use that seed value to generate a random number that's unique to every time it runs through this for Loop so what we'll do is we'll make the seed equal Frac fractional values of the seed times one two three four five six this is going to times it by some number and give it a random seed value every time or not random but I'd new value for our seed every time it runs through one of these iterations of this for Loop so you don't get circles placed on the same spot twice it'll kind of be a different number each time now we're going to do is make a new variable float2 called random offset this will be our random offset produced from that seed variable so make it a lerp which is a blend I will take offset range X which we have here offset range when I say x it's referring to this first number y will refer to second number so offsetrange.x and then offset range dot y and then our seed value as a alpha or opacity now seed value you might be wondering how's this working well since we used fractional here even if this number c times one two three point four five six produces some really crazy value like five thousand or two thousand or 157 how can that number be used as an alpha well since it's wrapped in a fractional node a fraction will only take the decimal points of it so it ignores what the number actually is and always gives me a number between 0 and 1. so I never have to worry about that Alpha going negative or that Alpha going over values of one so that fractional value can be really useful if you need to take large random numbers and just extract something that's clamped within a range of zero to one now at some point the radius of the circle or ring that we're drawing we're going to want it to grow and expand outwards as time passes but we're not going to worry about the animation component for now we want to get our placement working and get everything else working before we even worry about that so I'm going to block in a value here another float called radius not radius Min not radius Max just radius I'm just going to give it a fixed value for now later on we'll add a bit of Animation to the value so with changing but for now I'm just blocking in a number so we can use it and later on add the animation the next thing I'm also going to do is produce another variable called offset which will be our kind of final offset again float 2 for x and y and offset will equal our UV space minus 0.5 so what that's going to do is make the center point of our UV 0 instead of 0.5 and 0.5 and then well minus whatever random offset has been created or whatever those numbers have been and that gives us our final offset and then instead of using Point distance equals length UV offset we're just going to change it to length offset and then we have our Alpha here we got our smooth step radius men we're going to change that now to just radius this fixed value here that will eventually be animated and then what we're going to do is minus not ring thickness we're going to minus fade inner that value that we added to control the fade on the inside of the Ring and then for the second parameter here we're going to do a similar thing radius plus fade enter oops enter there we go and then point distance and then if Point distance is big or equal to not radius anymore or radius mid which is radius and not radius backs just radius and then ring thickness ring thickness okay plus equals Alpha and then return the result so what we're getting out of this is produce a different seed number use that seed number to produce a random offset you use that random offset to give us our final offset which we're going to use for drawing where the point or circles are so when we run this code now and unreal assuming I have no mistakes here we should get our circles or our Rings being plotted all over our EV space so it's not just in the center anymore so let's paste this in run it okay no errors there we go we got circles Rings placed all over the place they all have that little fade on the inner side and does our fade inner control work so .001 or point zero five point zero three okay so it's within the range of our minimum and maximum then we're going to see a nice fade if it goes over our radius Max as it's going to clip off so if my radius Min is 0.05 my radius Max is 0.1 if our fade is something like zero point zero one there we go we see a nice little fade there now we have something to control that so you need to take into consideration your minimum and maximum otherwise that that fade might look a bit Sharp but now we at least have a control to adjust that now I'm just going to leave it at a default or whatever value I had it originally 0.0 5 and it doesn't look too bad we've got nice little Rings plotted all over the place so next thing we want to do now we got the random position working we need animation and a radius to grow as time passes so we need to introduce time now sometimes in unreal time can be difficult to work with and it's going to show us as a quick example if I create time and I preview it it just shows this really bright color because just a number that's constantly changing if you want to debug that number we can connect our time to a debug float node I'm just going to preview this so you can see when it's resulting so that's the time it just keeps going up as long as unreal is running the numbers just keep going up and it's hard to work with that because it could be 50 000 it could be a hundred thousand it could be five thousand you could divide it by numbers you could do all kinds of things but again that fractional node can be pretty useful because I can just run this time through a let's stop this for a second run it through a fractional then put it in here then preview it now it just extracts the decimals it's giving me numbers but it's always going from zero up to one and then back so that can be useful if you just need to get some decimal point extracted from a bigger number it's kind of always within that range of zero to one so makes a lot easier to deal with so I'm going to use that to make sure our time values don't go don't go crazy now if we want to add time to our custom node we need to input for that so let's add that let's go to our custom node here let's scroll down here add another input call it time and then connect up our time we're not using it but now we at least have the ability to access that time note or that that variable that constantly goes up higher and higher so now let's take a look at how we can start to modify our code and introduce that time to make each one of these circles or rings be plotted as a smaller ring and then grow or expand outwards as time passes and then have them fade off gradually so we'll go back to our code and we need some sort of control as well to control how long that duration or cycle lasts that Fades offer grows those Rings or circles from a smaller size to a larger size so I'm going to add one more variable up here float duration let's make it equal one for now and then we're going to go back into our for Loop I'm just going to cruise over this code make sure we have nothing else a problem here so we get our seed value randomized or just produce a new seed value and then we do a random offset okay so after we do our random offset we can use this random offset to also randomize our animation a little bit so I'm going to create a new float called cycle and make that equal our duration that value appearance one it will take that and we'll plus it to a random offset it's a Rand offset we don't know what random offsets producing but just to make sure it's not like a crazy large value or something this may not be required but I'm going to do it for safety I'm going to throw into a fractional node so we get some decimal values between 0 and 1. and another thing you can also do if you need to collapse something between zero and one but cheaper than a clamp you could also use saturate but in this case I'm just going to use Frac for this that should be fine and then I'm going to create another float called pulse so our cycle gives us our duration and then a random offset that gives us this duration plus it adds some additional decimal value just to give it some variation so it's not always one it's gonna be like 1.3 1.2 1.8 it's going to be a little bit different each time and then our pulse will equal a fractional of time divided by our cycle so this will give us some kind of totally random decimal value between 0 and 1. and then what we'll do is we'll modifier radius here so we already have a radius so right now it's fixed value of 0.05 what we're going to do is radius is going to equal radius min Plus whatever this pulse value is so it's making a little bit of a different size multiplied by a radius Max minus radius min and that's going to give us a little bit of a different radius and since it's controlled by that pulse value that has that time in there it's going to change the size of the radius to kind of grow outwards and expand and now that we have all of that we have our offset here Point distance our Alpha and then we have our checking to see if we're Within the Rings minimum and maximum plus any sort of thickness we've added and that gives us our result and to make sure our result is within the range of zero to one I'll use saturate and just throw it inside there and then if we take all this throw it back into unreal let's see what we're going to get now with that animated pulse value and cycle okay so now we have our Rings they spawn they spawn smaller when they grow outwards and then they kind of pop off and disappear right now they just we see them disappear they just disappear they don't fade away which kind of makes the effect not as nice as we'd we'd hoped so how can we have these fade away well we need to taper that alpha or opacity over time so let's take a look at how we're going to be able to do that so what we want to do now is when these circles reach a size limit or when they reach their maximum size uh we want them to fade off not just pop away and disappear and respawn so we're going to multiply them by a value that will reduce their brightness as time passes and they get larger so if we go back to our code the way that we're going to implement this and there's a ton of different ways you can do this this code is not really the most optimal we're kind of just going through it producing something and then after we can always go back clean it up make it into a function optimize it but the hardest thing is getting it working so the easiest way I can think of controlling the fade of our rings without reworking everything we're totally to taking a new approach or really just the most simplest way is just to multiply it by value if you multiply anything by zero it becomes zero so we can just take our Alpha value down here and multiply it we could do something like Alpha multiplies equals so it multiplies by something and signs it as a new value and we can put something in there that will control its opacity if it's Alpha multiply equals zero it will instantly go to Black and not be visible if it's one it'll stay white if it's 0.5 it will fade it off to gray so we can control the overall strength of this by just multiplying it so what we're going to do I'll leave that empty for now we're going to go up here we're going to Define yet again another variable we're going to call this float fade outer so we're going to add a outer fade and we'll just make this a really small number for now and now that we have that variable we're gonna add one more variable in here called radius limit which will be the limit of when it reaches a certain size that's when it's going to fade off it's going to help us control our fade that we're going to apply to that alpha or that multiplication that we're going to apply to our Alpha so what we're going to do is under oh we could put it really anywhere but under his point distance here I'll make another float and I'm going to call it radius limit it'll be a radius min and then what we're going to do is add seed y so it's just a little bit of randomization in there at least some random decimal number again our c is going to give us fractional c times whatever it is so it's just going to give us a little bit of a variation and then we'll multiply that by radius Max minus radius in so that's our radius limit and then we're going to use that here we're going to multiply our resulting Circle or ring by a smooth step and we're going to do radius limit minus fade outer so we can control that fade and then radius limit again plus fade outer and then point distance and actually this might give us some unwanted values so we're going to put in a saturate so it clamped between zero one and we're going to start with one and then minus or a smooth step now why do we do one minus our smoo step and then all this radius limit stuff is because if we just have a smooth step it's actually going to fade in it'll still pop off when it ends by doing one minus we invert the mass were kind of the the value that we're producing so by inverting it it'll help it fade out and not fade in so if you wanted to fade in instead of fading out you could leave it as just boost up and then whatever we're doing if you want it to fade out then we do one minus that inverts everything or inverts the value so we end up with the opposite value so it's actually fading out that we have that kind of have our full set of code here we'll copy it throw it back into an unreal see if we have any issues right now everything's just popping off if we throw this in here and hit enter okay so there we go now when the circles fade off see how they fade off from the inside and then go to the The Edge and they get smaller small and smaller and then fade off so it's a nice little effect that makes it get thinner and then disappear so when you watch it from far away it feels much more gradual so it's a nice little bit of a fade and if we have that thicker or thinner it'll also kind of adjust to work with that and then we have our duration control here which we haven't played around with but we put it to like half you know you can control how quick they fade off really fast or much slower so there it is our final result again you can play out this code modify it probably optimize it further and make it a bit easier to look at as well but this is kind of a bit of a walk through on how we can start to make these kind of effects adding a bit of Animation taking a simple shape playing with some smoothing or gradients and it can start to get these more complex effects that would be hard to do unless you use an animated texture but now we can have it all done procedurally so that's pretty cool and if you're interested in more of this stuff check out the rest of the videos that I have on hlsl coding with unreal and there might be a few tricks or things that you pick up there and I'll definitely add some more videos but if you've enjoyed this video If you learned something new make sure to subscribe like this video and if you're part of the patreon which there's a link to in the description below you'll also get access to the PDF for this video which goes over all the steps in this video in a PDF form in a little bit more detail and you'll also get the source code or the code for this Shader as well so check that out if you're interested otherwise let me know in the comments down below what you were able to create with this or similar code to this and if you have any questions
Info
Channel: renderBucket
Views: 6,787
Rating: undefined out of 5
Keywords: Pseudo Random, Unreal Tutorials, Unreal 5 Tutorials, Unreal 5 Shading, Unreal 5 Custom Nodes, Custom Nodes in Unreal, HLSL In Unreal, HLSL Tutorials, Computer Graphics Programming, Unreal Technical Shading, Procedural Shading, Raindrops, Ripples In HLSL, Patterns With Math, Custom Unreal Shader, Shader Coding, Unity Shaders, Game Design Materials, Drawing Shapes With HLSL, Unreal FX, Unreal Shader Coding, Coding Shaders, Unreal, SmoothStep Function, Advanced Shading Unreal
Id: I4b1Jj_VIf8
Channel Id: undefined
Length: 35min 43sec (2143 seconds)
Published: Mon Sep 11 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.