BotW STYLE GRASS TUTORIAL

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
since I was asked a lot I'm going to show you how to create this breath of the wild styled animated grass shader so we are going to split this up into four steps in the first one we're going to create the wind shader this is going to tell us how we should deform our grass mesh at the specific point of space in the second step we're going to create the grass geometry just a single blade of grass and in the first step we will use instancing to actually populate our patch of grass and then we are going to put it all together okay let's start with the wind shader we're going to need a bit of geometry so we can preview the shader as we work on it so let's create this ground mesh and let me align it with y 0 just like this ok and let's create the shader material and save it right away it will be a bit easier to manage this later on and let's do the same thing for the shader itself let's call it wind shader and we have to define the shader type this kind in disguise it will be special and let's create fragment function just to test if that indeed works okay we have to define the noise function which is going to tell us how much force we need to apply to our class measure the specific point of space and for that we'll be using the word annoys this is rather simple but very useful noise type which for every P we supply and some arbitrary set of points is going to find the minimum distance and let me show you how we're going to calculate it in this very simple predefined sample and then we will turn it into something more manageable so let's define a couple of points like this but see maybe something in the middle and we're going to need reference distance which starts on one at one point oh and let's step through our array and for every point let's update the distance of minimum between distance and length of difference between P and the point we are testing and we return the distance and all we have to do is call it right here and let's test with UVs and here you go so the problem if this code is that we are using only three points and if we want to cover some large area this is not enough but also if we would like to pass some variable number of elements we cannot do that because in shaders our loops need to have predefined number of steps basically you cannot have this as a variable and this creates problem for us fortunately smart people have figured out the way around this and what we need to do is tile our noise so to the simple multiplication we have created the grid and now if we modify the code and ensure that in every cell of discreet there is only a single point we can then modify the loop so it steps over this free by free region and calculates the minimum distance out of it we are not going to need most of this function anymore we will remove all those pieces and the first thing we need to do is split our P into the integer portion of it by taking the floor and we need the fraction portion of it and obviously we'll be using fact for that let's ignore the integer part for now let's create our loop which is going to step from minus 1 to 1 on both axis now we need to actually convert this into vector2 and we can calculate the diff and this is the place where we going to put our reference point for the cell but we don't have this data and let's redefine this as the middle of the cell for now and we have to subtract the fraction of P and again we're updating the distance by taking the mean of taste and length of difference okay so it's more like it so the only thing left to do is to replace this reference point in the middle of each cell with some randomized point based on the cell index so we have to create random function that is going to return random point in the range between 0 and 1 and we will input some kind of P vector so one way to do it is to use that products and I'm going to write it real quick and explain what am I doing here so we want to take the dot product between P and some arbitrary vector for both access and taking the dot product between some P and some other vector and in mind these are not normalized this is design of the unit vectors so we get some number out of this it's not going to be in the range of minus one to one it's going to be some number and to wrap it up into this range of minus one and one we need to take sine of each axis but obviously this is still not in the range between zero and one and to do that we take fraction out of it and fraction is always positive number what we can do more is to create some kind of offset for the sine but you know we don't have to do that and now let's plug our random function here but instead of using this predefined point we are going to take our integer portion of the P and let's add n to it okay and this randomizes our points and now we can actually animate this if we add time multiplied by some direction like this and this is basically our wind function here with the wind side already let's look at the grass geometry and for that we'll be needing only a mesh for a single blade of grass and we could prepare this blade of grass with blender or something but you know let's do it in a geeky way and create the script so let's call it the mesh factory and this mesh Factory is going to extend the object and expose static function simple grass this will return the mesh object and we'll be using array mesh to construct it and for that we need to prepare arrays of all values per attribute we're going to need so we obviously need vertices so pull vector free and also movies well not really we need a place to store a parameter that it's going to be used to decide how much of wind influence we're going to apply to a single vertex you see we want only the tip of the grass blade to be affected by the wind we don't want the base of the mesh to be affected at all and our mesh will be very simple in this case we could create something more complex but we don't really need to we'll be making a single triangle and that's predefined all the points since this is very simple triangle nothing more and since we start with the base our UVs here will be at zero unfortunately there is no other attribute with that takes less amount of memory we cannot have like single float here and the second point of the base again movies here will be 0 and 0 and now we need to define tip so the tip will be right in the middle like this and ok and at the tip we're going to have full force of the wind ok now we need to compose our arrays into a master array this array needs to be reformatted resized to fit all the other attributes all the channels and we need to pass our vertices and we need to pass our UVs and we can either do it as pass it as UV or UV - let's use UV - because it seems like a bit more appropriate and array mesh and you need to add surface from arrays and primitive triangles since this is like only one triangle and let's set the custom access line bounding box so we need to define the min and Max position here services rather simple it's just something like that and something like that sorry like this okay and let's return our mesh and this is basically it our mesh is ready now we can actually do the instance in part so with the mesh factory done let's plant some grass let's create another script let's call it planter and for this note we'll be extending multi mesh instance let's remove all of this and I want this to work in editor mode so let's define it as tool and we need to set up some properties for the inspector we're going to need size of our grass patch let's call it span and let's redefine it too we also for every properity we're going to define setter methods so we can actually update our instances we're going to need also the count of our blades 1k should be okay default parameter here again we need setter function method and next we're going to define couple of properties that will be used to randomize the grass patch a bit we'll be setting weight as the range between some main and some max the vector to is just the data structure to hold it hold it we would have to create another data structure just for that it doesn't make sense let's use the vector two here set weight and same for height maybe something like that it keeps complaining about the satyr functions not what defines but don't worry about it and apart from weight and height we are going to define sway angles so we don't solve the grass doesn't move in exactly the same way so it adds a bit variation to the grass movement the same for pitch it can be also 10 degrees okay so before I define the setters here I will create the empty rebuild method it's not going to do anything at the moment but we will call this method in every time we need to update the instances so on the init on ready and also for every setter method so let's start with spam [Music] just like that and I will repeat the process for all the other properties here but I will fast forward it because it's like super repetitive no point in your sticking around okay we are ready to fill our rebuild method and the first thing we need to check is if the multi mesh object exists this is where all the instance data are start and if it's missing then we're going to crash and also let's make sure that no instance data is carried over from the rest last rebuild so by setting instance count to zero it clears everything now we need to set up the mesh will be using four instancing and yes this is the moment we need to use our mesh factory we're going to preload it and since its method is static we can just use it without instancing the the factory itself okay next we need to set up the data format for our instances and the first one is transform format which is obviously 3d and we need to do this before we actually define even the number of instances we need to set it up before we do anything else we're going to need also the the custom data format this will be used to store the blade of grass size angles and and so on and we can also use colors but we are not going to need that here okay and now we can set the instance count to our account okay and now we can step forever index in range between zero and Multi mesh instance count we're going to need position or origin for our blade of grass and let's use round range and let's use - span + span for why it will be 0 and Z will be also random next we need to set up the rotation and it will be simple we'll be using the word up vector and we need to convert to radians and again randomized between 0 and almost full circle and now we can set the instance transform and we need to pass the index and the transform which we get from we create created with basis and the last thing we need to set is the custom data structure and we'll be using color for that but again this is like the color has four flows and we need to pass four floats so this is why we're using color and first let's pass the weight of our blade of grass then let's pass the height and then the angles and you can do it in every order you want as long as you receive it in the exactly same order interpreted in the same order on the shader side and exactly the same for yo okay seems about right just don't forget to save okay let's test it out and we have to create multi mesh instance let's call it grass and we need to assign our planter script and as you can see it kind of works it doesn't look like grass yet but the planter does what it should and once we actually set up the material and the shader in the next step we'll be done with it let's create grass material and we have to assign it here in the material override and this will be a shader material let's save it right away and let's do exactly the same thing for the shader the shutter type will be special and the first thing we should fix is a scale problem at the moment every blade of grass is rendered with this same weight and height which is too large and this doesn't look anything like patch of grass so let's copy the current vertex data from this building variable variable and let's scale the vertex by instance custom data and at the end we have to reassign it to the built-in variable so this instance custom structured this is the representation of custom data assigned here in the planter script so this becomes X Y Z W and it is accessible through this instance custom structure if you look closely at this mash you will notice that the blades of grass are rendered one-sided at the moment so let's correct this right away we can do this in the material or we can set up the flag right here and this will be cool disabled so now you can see that grass is rendered with with both front and back and another thing that is missing in our mesh is the normal so because we are making this stylized grass we can assign this up vector like this and as you can see it doesn't work 100% correctly as we would expect so some of these blades are rendered with exactly the same color as the ground here which is correct but other blades are rendered incorrectly and the reason this happens is that when you render the two-sided geometry and you render the backside the normal gets reversed so if I reverse it like this you can see that we are reversing the normal so other half of them is rendered incorrectly so to fix that we have to use front-facing variable this tells us if we are rendering the front or the back of the mesh that this variable is either 0 or 1 and we actually want this either plus 1 or minus 1 and all we need to do is multiply normal by this and as you can see now every blade of grass is rendered with exactly the same color which obviously makes it hard for us to see anything so let's add two colors that we are going to blend together across the blade of grass and let's make the top color white and the bottom color black and here inside the vertex shader let's compute the final color by mixing those two and we'll be using for that the wind influence parameter this comes from our mesh Factory these properties we put here in the UVs so all that we need to do now is to output the albedo like this it looks good but of course we need to assign some real values and since we have defined this as the uniforms we can simply set the parameters here and I have already preset two shades of green we want some lighter shade and some darker shade filter pattern and to make it blend correctly with the floor let's create new material here and disk in this case we can use default spatial material we've just our bed albedo set to the darker shade it looks bit dark so let's create environment perhaps some kind of sky and let's ramp up the ambient light energy ok seems about right so I guess this is a good moment to port our wind function from this shade that we have created previously so all we need to do is copy the worldly and random function and here in the in the wind shader we used to pass the UV space in the in the world function but here in the grass we cannot really do that because our geometry is instanced and it doesn't share the single UV space fortunately there is a way around this we can kind of calculate the UV here from the word position of the vertex so let's create the UV variable and let's multiply the word matrix with vertex position and we will pass here - one otherwise our noise will flow in the opposite direction and let's extract X and Z here so now if we comment this out and instead we will compute the worry for the our UV and of course I forgot that this needs to be color 4 so let's correct this and wind with I can see that it works but to make it more apparent let's raise it to the power of two and yes this seems to be working we could actually also test if it animates and perhaps we could increase the scale of our worldly noise like this this will make it tile more and to animate this noise we have to define couple more parameters so actually let's turn this into wind scale like this and let's define the wind speed and we also going to need wind direction vector free in the word space coordinates and we will make it flow in forward in the in the word space and one more thing we are not going to modify the Y compound here this is going to make our calculations more much simpler and since we are going to input the wind direction manually let's compute the wind direction normalized and let's compute our scale time like this and all that is left to do is to add to the UV our wind direction normalized X Z because we want like to the plane here multiply it by time and as you can see the whirling noise is animating it moves over our patch of grass okay and one more thing we are going to need is utility function that is going to compute rotation matrix from work for us from axis and angle rotation around an axis and for that we'll be using something called Rodrigues formula but the math behind this formula is way beyond the scope of this video so I'm going to fast forward this part but the takeaway here is that this matrix is representation of rotation so when you multiply any vertex or any vector by this matrix it's essentially like you would rotate it and we're going to need this because first we're going to rotate our blade of grass towards the direction of the wind and then using the wind power we have calculated we're going to bend this blade of grass towards that direction okay and let's test it out I'm going to define rotation axis the default one default ones here it's not really required but it's more tidy this way we need up and right-hand axis and what we can do is rotate the vertex around the y axis up vector so our vertex will be calculated with this function we just created we're going to use up vector and multiply by vertex and as you can see the blades are rotating around y axis I'm going to replace this one and we can remove these test lines I'm going to define the sway angles and these are going to control how much our grass is going to the farm let's see something like that sway five degrees end so these are like the base value for the material and here in the planter we have created these two values that will be kind of to make the each plate move in the kind of unique way and since these are in the degrees we need to convert to radians so we have to define the PI which is bit weird though should expose these for us but you know what can you do and all we need to do is multiply our degree angle by this and this is going to give us the same angle in radians and since I'm already here I'm going to define varying flow wind this will replace our wind variable down below this is because I want to use the wind power in the fragment shader to add some final touches but without the wind it will be impossible perfect so now let's make sure that our wind force is scaled by our wind influence factor encoded in the UV - now we have to convert our transform our word space wind direction to the model space and for that we're going to inverse word matrix and we're going to compute wind forward in the local space by multiplying wind direction normalized and to get the right-hand vector we're going to take cross-product out of wind forward and work up vector this is why I didn't want to touch the y component in the wind direction otherwise this will be more complex and now since we have the rotation axis now we need to calculate rotation angles so let's calculate the Sweden sway pitch first and we're going to take the grease way page convert to radians and we're going to multiply by wind power and at the end we're going to add instance custom Z this is the default value we have passed in in our instance data and for this way yo this other sway is going to waive price of late back and forth we need to again calculate the sway y'all deck convert to radians now we're going to multiply this by sign of time and multiply by wind power and at the end we're going to add instance custom W ok now all we need to do is calculate the rotation matrices so let's start with bending the light of grace grass forward and we're going to use our axis angle function the angle is ready and the axis here is wind right and the other rotation will be root forward and again our utility function and this time we'll be swayed yell and wind forward perfect so now here we are skating our vertex and the last thing we need to do is multiply rotation right multiplied by rotation forward multiplied by vertex and this should be it and as you can see the blades of grass are moving and I guess this is a good moment to add the final touch we'll be using the wind we have already calculated up here so let's keep the specular at 0.5 and let's calculate roughness by clamping two ends between zero something like that and all that is left to do is to adjust parameters let's increase the density here oh yes oh yes so thank you for watching I really hope you have learned something today and if you can't live without the project files you can find a link in the description below have a great day and see you next time
Info
Channel: lonegamedev
Views: 32,705
Rating: undefined out of 5
Keywords: Tutorial, Grass, GodotEngine, Shaders, BOTW, Stylized, Procedural
Id: usdwhhZWIJ4
Channel Id: undefined
Length: 50min 12sec (3012 seconds)
Published: Wed Jun 24 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.