HYPER-REALISTIC Grass Wind Animation [UE4, valid for UE5]

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hi guys as promised in this video i'm gonna show you my own take at achieving a convincing grass wind animation before starting i want to illustrate the rules i imposed to my approach to have a direction on which i could base it like a real work scenario i wanted every part of the shader to be entirely procedural so no backed data in vertex color ubis or textures everything is obtained with pure math for how much it was possible i tried to replicate as realistically as possible the movement of a grass blade moved by the wind so i tried to keep the stylizations to a minimum because of those conditions i also chose to just model the bare minimum required for the mesh to be animated which is just at oscillated vertical rectangle moreover given these constraints i ended up creating a next gen animation which is a cool way of saying that i didn't care too much about performances this time but it didn't came out too bad after all consider that i'm running this scene on an old 980 ti so with that of the way let's jump right into it the very foundation of my shader is the way i bend the mesh once done that in a convincing way is just a matter of getting the oscillations right so how to procedurally bend the mesh in a way that feels realistic enough and not less important the calculations required are actually implementable after testing out some stuff and trust me i did test out some stuff the best way i was able to find is to imagine the grass blade as a vertical segment and remap it along a quarter of circumference even though i'm still convinced that parabolas or ellipses would have been better shapes visually the circle has trigonometry on its side which makes everything much much more doable let's elaborate a bit more on this idea then [Music] what does it mean to imagine the grass blade as a segment and then remap it on a quarter of circumference let's imagine this line as our grass blade you can imagine the vertices that compose the mesh to be somewhere along this line now imagine to have a quarter of circumference that is tangent to the base of this segment i want these vertices to find their place on that like the midpoint on the mid the quarter point at a quarter etc and that will do for the arriving position but what about during the in between how can we calculate the vertices position when the let's call it bend intensity is not a full power in other words what's the path those points have to follow to go from the segment to the circumference section we could naively think about linearly interpolating the two positions which would definitely do the job but it wouldn't be accurate see if i draw straight lines from the start position to the end position we can easily see that the length of the leaf wouldn't be preserved actually you could clearly see it if i was precise enough with my draw but trust me on that it will become shorter while it travels to bounce back at its correct length towards the end we should rather look for some curves to use a spot for these points after many tests and tears also was showing earlier i came up with a neat trick to solve the problem if we make the circumference progressively bigger and bigger and each section on which to remap the segment on smaller and smaller it gradually approximates to a straight line which would eventually become identical to our starting segment and that's the theory which looks like something that should work but now the question is how the hell do we implement such thing let's imagine that our grass leaf has a generic height that we'll call h [Music] and it's located in the word at an origin o we know that this segment must be remapped along a quarter of circumference a section of circle that will call s since the segment has to cover the entire circle section and keep its length constant it means that the two lines must have the same length which means that h equals s another thing that we need to know is where this circumference is located in the world where its center c lies on for how we decided to position this circle earlier we know that its y position is equal to the grass blade origin and since we are talking about a circle we can assume its x component will be origins x plus the radius r of the circumference to know what the radius value is we can do some simple math we know that the perimeter of a circle is calculated by multiplying the radius by 2 pi which means that if we know the perimeter and we want to calculate the radius we have to divide the first by 2 pi now since we know how long a quarter of that perimeter is we can say that four times that divided by two pi equals r which simplifies to r equals 2h over pi let's see if everything did so far actually works this amazing website lets us to plot every function we want in a nice and interactive way let's bring all our formulas here and see what we get [Music] [Music] looks like it's working as it should if you remember though we said that the circle should get bigger and bigger and further and further to animate the grass blade properly but now we just have a fixed value which stands for the completely bent state this means we need to be able to shoot the center of the circumference from here to infinity at will which means the value of the radius has to be free to move moreover given this it also means that the length of our segment h won't always be equal to a quarter of circumference but to a section that gets smaller and smaller with increasing of r so what we need now to understand depending on the radius value how long the quarter of circumference s is we can do that by returning on the equation we use it to calculate r in the first place and putting 4s instead of p and simplifying [Music] do [Music] do [Music] okay we remixed things a bit and we are almost ready to move to the next step one thing that i want to fix first is the range of r having a parameter that goes from 0 to infinity makes playing with the graph quite uncomfortable and will make uncomfortable tuning the shader in a reel afterwards too wouldn't it be much better if we had a bending parameter called b that ranges from zero to 1 and linearly controls the animation [Music] to do that we have to change our mind again sorry about that and express once again the radius as a function of h and add the parameter b to it dividing for a number between 0 and 1 such as b makes the result bigger and shoot to infinity the closer the parameter gets to zero moreover by rewriting once again the value of s as a function of r we can spot a nice simplification we can do if we expand r in that function we notice that this nicely simplifies to just being equal to the ratio between h and b and since it is pointless to do that division twice we can now directly use s to calculate the radius [Music] we've been bouncing back and forth quite a lot i hope i didn't lose you there let's update the graph in desmos and see if everything still works [Music] it works like a charm we just have an error when b equals 0 since invalidates the division but we cannot care for now we'll handle that case once we move to unreal to build the shader the next step is to calculate the exact position of a generic point p on the segment projected onto the circumference section so again what we now want to calculate is another point on the circumference called pc tied to the position of p on the segment if for example p lies on two thirds of the segment length pc should lie on two thirds along the arc described by the circumference section to approach this we have to leverage the direct correlation we just noticed between the vertical distance from p and o and the angle along the circumference the idea would be that since we know that the value of s corresponds to an angle of 90 degrees on that circumference which equals to half p in radians then p y will correspond to another angle alpha which is exactly the next thing we need to find since there is a direct correlation between them we just need to impose that their ratios are the same and solve for alpha [Music] actually since the y of p is expressed starting from the y of o we should expand p y subtracting from it o y [Music] thanks to this angle we know in which direction the point pc is starting from the center c and since we know the distance from that point 2 which is r we can say we found it [Music] [Music] note that the cosine must be negated because from c we have to move to the left returning back towards the origin [Music] on paper everything looks fine to me so let's head back to desmos and update the graph to check if everything's done properly [Music] do [Music] it works and seeing it move also feels pretty cool so we finally know how to project each point on the circumference but we are still missing something i know i say that every time looks like i'm about to wrap up a concept to work out the math we approximated the grass leaf to be a segment but it actually has a thickness [Music] that means the vertices in the actual mesh won't be lying on the line but will be at a certain distance from it which means that their projected version won't be lying exactly on the section of circumference 2. they will be offsetted from the curve exactly as they are from the segment to start accounting for that we can add the generic vertex position v and express p as it was a projection of v on the segment let's quickly add this change to the interactive graph [Music] [Music] so now is time to figure out how to find dc the position of the vertex v once the grass leaf has been bent we can start by noticing that if v and p are connected by an horizontal line which is orthogonal to the vertical segment then vc and pc may be connected by a radial line that starts from the circumference center c and passes through p this implies that vc may be calculated exactly as pc but using a different radius value if we project v on the same level of o in fact we can realize that we can simply add to r the horizontal distance between these two points and keep everything else the same [Music] [Music] moreover we immediately realized that we don't need any more p and pc since we're just interested in the final vertex position and there's nothing that needs them to be calculated let's update the graph once again here i'll actually keep pmpc for nice visualization purposes [Music] do [Music] do [Music] [Music] let's tidy up a bit this mess to wrap up everything [Music] if we scan accurately the formulas we can also notice something interesting in how alpha is calculated it may be a bit tricky to see but here we're actually multiplying v y minus o y by the reciprocal of the radius so we can just divide that difference by r and scrub the variable s since it now used only to calculate the value of r [Music] let's head back to desmos once again for the final graph of date [Music] [Music] and that's how you do that card black magic it's now time to finally open a reel and build the shader or maybe not maybe you already noticed but until now we have been working in just two dimensions transposing everything as it is in a 3d environment will result in the wind able to blow only along one axis luckily we don't need to do again all the math work to account for this but just add few adjustments here and there imagine that now we're looking at the scene with the top view this point is the grass leaf and these are the word x and y axes the idea is to grab the horizontal components of our 3d points x and y and transform them as the reference axes where the wind direction which now is a 2d horizontal vector and the wind tangent direction another horizontal vector orthogonal to the first one that way we can keep calculating the wind as it was 2d and the wind tangent will store the vertices distance from the bending plane so that we can add that back at the end in other words we could now see the process like not bending a line on a circle anymore but like bending a sheet of paper on a cylinder [Music] to do the transformation we just need few simple vector operations to obtain the component that runs along the wind direction we have to do the dot product between the vertex x y coordinates and that vector [Music] to obtain the second one we have to do the same operation but using the wind tangent instead which again is obtained by rotating by 90 degrees the wind direction vector [Music] [Music] and what will here we can take another look at our formulas and see if we can simplify our life even more for example we could do all the math locally to the origin o so that its value is zero throughout to do that we can just subtract it from the vertex position b right at the start to obtain a local vertex bl that makes possible a bunch of simplifications like not needing c anymore and the possibility of just using well components in the other formulas where o was used so now all our 2d bending process will look something like this [Music] where before this will have to do the components transformation to be oriented with the wind direction [Music] and after we'll have to recompose everything in our usual coordinates since a real uses the z-axis as up vector this last bit could be confusing but essentially the 2d x-axis has to become the first two components of the final 3d vector while the 2d y component will directly correspond to the z component in 3d and now i'll finally stop trying to melt your brain and implement all this stuff in a real [Music] [Music] do [Music] [Music] and that's it look at how bendy your grass blades are [Music] even if not exactly the topic of this video i'd like to now spend some time in making our grass a bit better looking our animation could be the absolute best but it would lose a lot if the rest of the shader doesn't place the eye first of all let's fix the height value right now we just eyeballed it to something that looks about right but we can do better sadly there is no way of knowing the bounding box of an instant mesh in a reel that would have been perfect since the last component would have given us exactly its height instead we'll try to do our best to work around this limitation we can start by looking at the height of the mesh itself as setting that as starting value this is the height of the mesh with its default scaling [Music] now we have to find a way to know if that mesh has been scaled when placed in the scene we can do that with a simple trick we can transform a unit vector that points in the dimension we are interest in from local space towards space and calculate its length this will give us the scale along the z axis of the mesh so we can just multiply it by the height parameter to know the exact height of the distance in the scene [Music] [Music] very well now we can start by assigning a nice green value to our base color with some random variants [Music] [Music] next step is to create a gradient that goes from black to white from the root to the tip of the grass leaf to do that we can divide the object's relative position by this scaled height [Music] [Music] be careful to keep this operation in the vertex shader since most of the data we're working with in the pixel shader refers to sunset mesh clusters then let's square the gradient to have a nicer curve and let's use it for specular roughness and damping occlusion making each time the required adjustments [Music] now we're going to apply a well-known trick to improve the look of the grass in video games turning the normals to the sky let's deactivate the tangent space normals for this shader and let's try [Music] at first glance looks like the grass has improved but the light behavior isn't that good actually it's like all the grass was put on a flat plain we are losing all the lighting variation the shape of the landscape should give us let's go get it back to implement this fancier version of the trick we'll have to follow a path that's a bit weird but effective we have to set in the landscape brass type asset the mesh to be aligned with the surface this operation will cause all the instances to not be vertical anymore causing some errors in our band operation too to fix that we can straighten them back up by a shader code before applying the band to do that we have to calculate in which position the vertices will be if the grass blade was placed vertically as before luckily this operation will be simplified a bit by the fact that our mesh extends only across two axes and the final position has to be perfectly vertical let's start from the object relative position and calculate at which distance the vertices should be from the mesh origin when in vertical position along the local x and z axis [Music] now the first component has to become a 2d vector to retain the random grass blade rotation in the scene let's grab the first two components of the transformed x-axis and normalize them to have that direction and multiply that by the value of the x and y components [Music] now we can append everything together and use these new positions as starting point for the bending operation [Music] and now as normal value we can use the tangent space y vector since is the one that points from the root to the tip of the grass leaf in its original position let's now take a moment to appreciate the variety we got back in our scene i'd say that now we should address the straightness of our grass which doesn't look too natural we can leverage the bending system we already got in place and see if we can find a way to bend each leaf in a different direction maybe we can set as bend direction the local right axis of each mesh after projecting it on the horizontal plane so that each mesh will be bent on its own side [Music] do [Music] nice now we should really break the monotony of the normals they just followed the landscape without adding variety to it to obtain what would be perfectly accurate normals for our bent grass we can leverage the fact that we are bending it along a circumference [Music] by multiplying the radius by the band direction we find the center of that circle in pivot relative space [Music] now if we subtract it from the positions of the bent grass we get a bunch of vectors that point from the circle center to the grass surface as rays let's pass them through the vertex interpolator normalize and multiply by the two-sided sign and now we can interpolate them with our old up facing normals to obtain the best looking effect we can find as final touch let's set the shading model to two-sided foliage [Music] that was quite a digression but we now finally got everything in place to start building a convincing wind which actually reduces to properly animate only two values bend direction and bend intensity before starting i want to point out something though the logic we'll use to build the wind will be kind of universal good for basically everyone that wants to implement it in his project the values of scale speed direction etc won't as they have been selected to obtain a nice result on my personal scene which will be surely different from yours so gather all the patience you have if the result on your side won't be that good right off the bat and take your time to adapt the shader to your needs let's start by creating a convincing gradient for the wind blows in this case since i want to keep everything as procedural as possible i'll use this noise as base [Music] let's also temporarily set the shading model to unlead to preview the gradient we're going to build to understand better what we're doing while we work [Music] let's use the object position scaled as needed as input the idea will be that where the noise is white the grass is fully bent by the wind and where it is black the leaves are near the rest position let's add a smooth step node to the noise which will change the contrast curve so that the wind blows will be more decided and will also remap the noise to zero one range at the same time now we need to animate the noise by translating it along the wind direction that we're about to define for a reason that i'll explain later i'm going to define the wind direction as an angle and not directly as a vector so the value of 0 will correspond to the positive x direction 0.25 to the positive y 0.5 to the negative x and so on let's now convert the angle to 2d vector by calculating its cosine and sine multiply this vector by a negatively scaled time the scale to control the velocity and the negative to not make the noise spawn backwards and add the result to the scaled object position i've added a pan on the third dimension too to make the shape of the wind blows change with time one last touch we can give it is to stretch it along the wind direction to improve the realism of the wind blows since the wind angle can be potentially set with any value we have to find a way that will always work we can't just stretch it let's say along the x or y axis to do that we can use the same approach we already implemented to our bending use the wind direction and the wind tangent to transform the object position x y components to a new base let's calculate the wind tangent vector by rotating by 90 degrees the wind direction [Music] and let's do the dot product of both these vectors with the x y object position vector [Music] and now we can just scale as we want one of these components to stretch the noise after that we can recombine everything back to the usual word space coordinates by multiplying these values by their respective vector adding the result together and appending back the third coordinate [Music] do [Music] now that we are satisfied by our gradient we can start using it to animate the grass let's set the shading model back to what it was before [Music] let's start by using the gradient as bend intensity parameter and to interpolate the band direction between the rest position and the wind [Music] [Music] [Music] do [Music] [Music] on the far distance i'd say that already looks quite good but when looking nearby the grass leaves are moving a bit weirdly [Music] [Music] first of all let's avoid them to completely straighten up when the gradient is black by remapping the range of the wind gradient secondly let's multiply that band intensity by the length of the band direction vector before it gets normalized [Music] this way the grass blades won't rotate around their people mechanically but will have a more natural motion like they are flipped over by the wind [Music] as last thing let's be sure we are never dividing by zero [Music] [Music] definitely much better let's have a look around even though it is quite nice you'll agree with me that we're still missing something the grass blades are missing some individuality they are too synchronized in the oscillation to add some variety we can add some little random offsets and some oscillations to each instance to do that we need some randomly generated values but as you may already know by default we are given just one of it and we are already using it for the base color variety there are probably infinite ways to generate pseudorandom values which are less or more accurate and expensive in this case i'll go for the simplest thing i can do and just use the object position components fractional part [Music] since the instances are placed randomly in the world we can leverage this thing to our advantage [Music] [Music] [Music] let's use the first value to make each grass blade to be a bit early or late in the wind blow animation [Music] [Music] after that i'd say to add some little oscillation to both bed intensity and bend direction for the first we can add a couple of sinusoids to the wind gradient one slower than the other which change influence depending on how strong is the wind in that moment [Music] [Music] easy as that and we can already see the animation improving in the scene [Music] now with the last random value we have we are going to do something very similar to the wind angle and that's why at the beginning i chose to express it as an angle first so that now we can just oscillate it without going through a more complex vector rotation operation so let's just add a very similar chunk of node to what we did before [Music] one more thing we can recycle this random offset to also make the rest bend direction oscillate we can use the very same value as time input for a rotator node and pass that vector through it and that's it everyone i hope you're still alive after this bombardment from deformation what do you think is that something you will try to use in your game how does it look to you do you have any doubts about this approach there is so much more we could discuss here we could still improve both on visual quality and performances i can also imagine some of you skipping a heartbeat when seeing a grass field made of individual grassleaves but more we move towards newer generations of hardware more this kind of things will become a viable approach to take and today i want to leave you with a challenge if you were using a mesh that is not an individual grass blade but a clump what would you do to make the individual leaves still retain their individuality when animated as usual let's discuss it in the comment section i'm very curious about your thoughts and maybe we'll go deeper on this topic in a future video [Music] you
Info
Channel: Visual Tech Art
Views: 27,083
Rating: undefined out of 5
Keywords: unreal engine, grass animation, procedural animation, procedural, math, vertex shader, material editor, grass wind, realistic, ue4, ue5, linear algebra, math for videogames, dev, game dev, tutorial, how to
Id: gfQNjC69PCM
Channel Id: undefined
Length: 50min 7sec (3007 seconds)
Published: Tue Jul 05 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.