Godot Shaders: How to Make Animated 2D Fog (Procedural)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

Great step by step explanation (aside from some "I have no clue why it works like this, but use this magic number 0.5").

It looks nice as a static image. However, is this really usable for an in-game animation? It doesn't seem to loop seamlessly and there's a visible animation skip every couple of seconds. Or am I missing something here?

👍︎︎ 2 👤︎︎ u/richmondavid 📅︎︎ Oct 07 2018 🗫︎ replies

This was incredibly straightforward and easy to follow. Thanks so much for sharing this.

👍︎︎ 3 👤︎︎ u/jscarlet 📅︎︎ Oct 06 2018 🗫︎ replies
Captions
I saw I was playing this game called dead cells and I couldn't help but notice the beautiful fog effects that was going on in the foreground the fog wasn't really moving as a whole but it was animated and dynamic of course the fog was just part of the post-processing but I wondered how can i implement this into my own game so I asked undergird I was subreddit and got some replies a user by the name of mr. egm told me that the fog in dead cells was basically just an animated image texture that they got and overlaid on the screen okay I tried looking for an animated noise texture online but unfortunately I couldn't find one so then another user by the name of smelly hobo 101 however relates me to the book of shaders calm a website which teaches you step-by-step how to make shaders it was specifically an article on fractal Brownian motion which showcased a similar fog effect made through procedural shaders instead of an image texture so how to look and after about an hour of trying to comprehend complex math functions in shader code I can say that I've pretty much figured out how to recreate the fog so here's a tutorial on making dynamic fog and ghetto shaders it may seem like I'm really good at maths but I really just learned everything from the book of shaders website huge shout out to the website and its authors as this tutorial will not have been possible without it and I suggest you check it out as well link in description finally before we start this tutorial took a lot of time and effort to make so if you end up finding it helpful I'd really appreciate it if you left - like if you want to see more tutorials like this let me know what tutorial you'd like me to make and sub to the channel with notifications on so might just end up making it alright let's start creating some fog [Music] okay so let's set this up I've got an empty project here let's switch to 2d add a node 2d of course then let's drag this texture in as a sprite this is a screenshot from the mobile game Altos Odyssey it's a great game I suggest you check it out but I chose this image because the colors are quite flat and not too dark not too bright so we can easily see the effect of our shader on it let's just scale it so it fits within the frame save it as a new scene and select a scene as the main scene then let's run it and see what happens okay it pops up that's great so in order to add the fog on top we need to add another sprite on it so let's just drag the default ghetto texture on here and of course it doesn't cover the whole scene so the scale is something like 20/20 and that's better now the color of this texture doesn't matter at all since we're gonna be writing a shader to decide what color it is so let's go down to material here under canvas item under material let's add a new shader material click into that add a new shader and click into that so now we've got this window open where we can write our shader but first of all let's save this shader as an actual file inside of our project let's just say fog shader save that way you can easily reuse this shader for other purposes and you won't lose the file that's easily now inside of our shader the first thing we have to do is declare the shader type so shader type can this item canvas items the shader type that you want to use for 2d textures and not to render some colors we have to make a new function called void fragment the fragment is the function that the shader calls when it wants to decide what color to render so let's make a new color value here so to declare a field variable within a shader you need to use a keyword which is either uniform or function or varying we'll use uniform because you can then set this variable from one of your game scripts so let's declare effect for a color has four values color equals to BEC for bracket and let's just make it read 1.0 0.0 0.0 1.0 and a semicolon of course so these are all float values that's the R that's the G that's to be and that's the Alpha so this will make a color that is completely red and completely opaque now inside of a fragment function if we just say color in caps lock as you can see it becomes red so it's one of the constants within the shader language color equals to our color variable then as you can see our texture becomes red as you can see if we set the green value of the color to one our texture becomes yellow so you can change the color that way if we change the Alpha value to say it's your point five then you can see that it becomes less opaque so that's how you set up a shader in Godot so I figured out a nice purple color to go with our Altos Odyssey background the values are 0.33 0.15 and 0.8 - how did that as I searched up color picker on Google and I picked a purple color then I divided all the RGB values by 256 and that's how I got the float value to put into the shader language now the next step is to make some random numbers now unfortunately the shading language doesn't have a random function I don't think so we have to make one ourselves let's do floats Rand and the input that it will take is not one number but two numbers as we're generating random numbers according to a pixels coordinates on the texture so every pixel will have a random value so let's take in a vector to called coordinate right now let's just return 1.0 as a float to see what happens let's make the color actually effect three because the Alpha value we don't need anymore we're gonna decide the Alpha value with our random function as you will see later so the color will set it to bec four and it'll take in our color which is effect three and then one more value which is Rand which is the rand function and then we'll pass in our texture coordinate which you can get by typing in capsule UV and as you can see our texture becomes perfectly opaque if in the rand function we didn't just return 1.0 but we returns cord dot X then you'll see it's a gradient as the x value increases from left to right but this is a random function so how are we gonna do that we'll take this step by step all right first of all since there's a vector to let's get the dot product of the vector two and some other random vector to that we just make up on the spot like 10 10 as you can see it looks really weird but it works the reason why it looks weird is is because the Alpha value actually goes above 1.0 while we want it to be between 0 and 1 but we'll fix that later so the next step is to get this dot product and then sign it and as you can see we get a wavy texture which turns from purple to green and the reason why it's green and not purple is because the Alpha value goes below zero all we want it between zero and one but as I said we'll fix that later and then we get the result of the sign and we put it in a fracked function as you can see it looks like this now and what the frack function does is basically for any float it takes the fractional value of that float so say you have five point two five the fraks value will return 0.25 and not the five you have seven point three six the fraks function will return point three six and not seven so since it's now a fractional value it'll always stay between zero and one and we won't get any of that weird rendering that we saw earlier so as you can see this texture looks kind of a little bit random from the top left to the bottom right but it's not quite enough so what we're gonna do is inside of the fraks function let's multiply it by one thousand point zero as you can see it looks a lot more random now but we can also inside of the sine function multiply the dot products by another thousand which will make it even more random there we go but as you can see even though it's random from top left to bottom right it's not really random from bottom left to top right it only goes in one diagonal direction for some reason so this is why we need two inputs for our rand function as a coordinate if you only have one input for your rand function this is what you'll get but now that we have an actual vector to coordinate we can go into the dot products and instead of being ten ten we can make it something more random like 5678 and as you can see here it looks a lot more random from all directions now so what we've got here is a random function that takes in coordinates and returns a float value according to the coordinates so that every pixel which has different coordinates gets a different value which is why it looks so random of course it's not actual random as computers can't really make real random numbers this is just pseudo-random so the next step is to go from this random texture that we have into a noise texture now this may look like noise to you but this isn't an actual noise section so let's just make another function just like the rand function float noise and they'll take in the same input back to coordinates so it returns a value according to the coordinates of the pixel so each pixel will get its own value now what we're gonna do here is have two values a vector 2i and a vector 2f what I is going to be is going to be the inputted coordinates but we put it in a floor function now floor is just the opposite of fracks as frac takes the fraction bit floor takes the integer bit so say you have five point two five again floor will give you five and fracks will give you two and fax will give you point two five if you have seven point three six floor will give you seven and fracks will give you point three six so now the eye is the floor so the F is the fracked remember this is a vector two so this isn't just a float value it's two float values within a vector 2i stands for integer and F stands for fraction so let me explain this noise function to you say this is our coordinates and we want to find our value for that coordinate what we're going to do is draw a rectangle around it and the thing about this rectangle is that its corners have whole number coordinates so it's x and y values are always whole numbers aka integers which means it snapped to a one-by-one grid so we have a zero zero and then we've got a 1 1 and here we've got a 1 zero and we got a zero one and then for each corner we generate a random number say this one is 0.56 this one is 0.98 this one is 0.32 now we've got four random values for our corners what we do to get into the value for this point is just to interpolate from all four corners and now we have the value for our points what we're gonna do is have four corners of a rectangle and we generate random values for those four corners so let's do floats a to random I now remember I is the whole number or integer part of the coordinates that we get as an input and a will just be the random value for one corner of the rectangle we're talking about which uses that whole number coordinates let's copy that a few times four corners that's a B that's a see that's a D and BCD instead of being random I we have to draw out a rectangle here so B will be the I which is the coordinates plus one on the x axis plus effect to one point zero zero point zero so B is now the top right corner C is the bottom left corner so it's plus one on the y axis plus effect to 0.0 1.0 and now D is the bottom right corner so it's plus one on both axes plus back to 1.0 1.0 so now I got four random values for four corners of a rectangle I remember because we're using I which is the whole number coordinate that means the rectangle corners are snaps to being whole number coordinates so say our coordinate input is 0.5 0.5 then the rectangle we get is the rectangle that surrounds it which has the corners of 0 0 0 1 1 0 1 1 but we have a coordinate of 0.5 0.5 so that's where the F value comes in for interpolating so what the F value stores is the fractional part of the coordinates so how far it is from the actual corners of the whole number integer rectangle now let's use the F value here to interpolate our value for our points from the four corners of the rectangle now this is really complicated so just bear with me the value that we get for our point is first of all we mix a and B which is the top left and top right according to the x-value so how far we are in between them mix is just a function that does linear interpolation for us and once we've interpolated those two values we add C minus a so that's the the bottom left minus the top left times the Y so how far we are down times one point zero minus F point X so adding the difference of the bottom left and top left also depends on how far we are right because if we're on the right side of the rectangle we don't care as much about the left corners so we have to multiply it by one point zero minus f dot X and then we add D minus B which is the bottom rights minus the top right times F dot X as I said we have to see how far we are on the x-axis if we're on the far left then we don't care as much about the right corners and then of course the F dot why is it also depends on how far we are down on the y-axis and then we just return that so now instead of calling a random function when determining the color we'll call our noise function and see what happens and say we just get one big rectangle with the gradient in between that's because the texture coordinates for this sprite is actually just 0 0 1 0 1 1 and 0 1 this whole sprite technically is just a 1 by 1 square and everything in between is a float a decimal between 0 and 1 so the score is way too big for our liking so what we're gonna do is instead of passing in just the simple UV we'll make a new value here and back to cord this is our custom coordinate value equal to UV but we multiply it by say 20 so it's much smaller whoops and you gotta add that dot zero because you can only multiply by floats so now instead of passing in UV u is passing coordinates and what you will see here is that we got ourselves a noise pattern you might be able to see and some spots the corner of rectangle because the transition between gradients is pretty sharp and that's because we did a linear interpolation is what that means is let's say let's say we have a few dots right if we do a linear interpolation we literally just draw lines between those dots which is why the transition between these gradients seems so sharp what we can do though is a cubic interpolation so the gradient between these dots will actually be curved and not just straight lines now to be honest using a linear vs a cubic interpolation in this example will actually make much of a difference in the final result but just for the sake of teaching I'll show you guys how to do it so let's make a new vector2 here to replace our F vector 2 cubic equals 2f first of all and then let's change all the essence of cubics they see there's no difference because cubic just equals 2f but what we're gonna do is big equals to F times F so that's squared already as you can see there is a little bit of a difference already times bracket three point zero minus two point zero times F yeah I don't understand this as well but this is just how they do cubic interpolation x' instead of just using f you do F times F times three point zero minus two point zero times F but as you can see our result is much smoother compared to our linear interpolation results so there we have it we have a noise texture now so we've done a random function we built on top of the random function and we made a noise function we made a noise section now so now what we're gonna do is we're gonna build on top of the noise function again and make an f BM function float as it returns a value again f BM we take in vector coordinates again as it generates one value for each pixel and what f BM stands for is fractal Brownian motion so that may sound complicated but it's really not a fractal you may know it's a shape that has infinite detail so you add some detail and then you zoom in add some smaller detail then you zoom it again you add some more smaller detail then you zoom in again and add some more smaller detail and it goes on infinitely to the point where it has infinite detail so what this fractal Brownian motion thing does is basically similar to a fractal is to make a noise texture zoom in make a smaller noise texture and add on to it and the zoom in again make an even smaller noise texture and then add on to it so the noise texture can have infinite detail as well obviously it's a computer it can't really do infinite detail but we can define how many times it add smaller detail with a value called the number of octaves so let's make a uniform in octaves up here equals to let's say 4/4 is a good starting point so what we're gonna do inside of this function first of all we got to make the final value that we actually returned at the end so float value equals to zero point zero at first float scale because remember how the detail becomes smaller and smaller scale equals two zero-point-five at first I tried making the scale start at 1.0 but for some reason it just makes it completely different so let's just stick to 0.5 and I don't know why boluses so now let's add a for loop because we're repeatedly adding more detail each time smaller and smaller so for int I equals to 0 I is smaller than octaves and I plus plus that's how you make a for loop so inside of this for loop for each iteration we add some noise to it obviously so value plus equal noise enter the coordinates and then times by scale then after we do that we got to change up the corner and the scale a bit to make some smaller more detail noise to add on top of that so coordinate times equal to point zero that you can just literally just multiply it an input for your function and then after multiplying the coordinates multiply the scale so the scale becomes gradually smaller the coordinate value becomes gradually larger and that's just how you keep adding smaller and smaller noise to your texture so now instead of noise let's make it fbm and see what happens okay whoops I forgot to actually return the value return the value so now we should see that what we get is a much more detailed version of the noise let me just make the color white for you to see now this is our fbm as you can see a very detailed noise let's compare that to the other functions that we made Ryan just gives us a pseudo random value for each pixel and look makes it look like this noise makes our texture look like this and then if we add smaller and smaller noise for a few iterations with our fractal Brownian motion function will change the FBM it looks like this which is a lot better so now we must do two steps involving fbm first time we have to make an F BM texture that's constantly moving in a single direction let's get a moving value here so floats motion equals to FB m cord plus time and if we replace just F BM chord with our motion value that we just made you'll see that our texture is actually moving in a direction because the coordinates are being manipulated by us according to the time now yes I'm adding a vector two to a single value so how that works is it just takes this time value and makes it a vector to of itself so it'll be the same if we do vector to time so it's just adding the single value to a vector two but adding it to both the X and the y and if we want to make it a bit slower we can just multiply it by 0.5 and there we go we have a slower-moving texture now instead of having our value as a float as the final output let's make this a vector two that I'll show you why later but now let's surround this value with a vector two function back to now we've got the same value but as two values inside of vector two now you'll see it doesn't work in our color because obviously it's a vector two and not a single value let's just temporarily make another value here floats final equals to F BM chord let's just go back to our static value here final and as you can see it doesn't move now but what we can do is in the coordinate input of the FBN function while we're making our final value we can add on the motion what this will do is adding any texture to a coordinate input it will distort the final texture because at the start every pixel had its own coordinates an x and y value which was correct the pixel should be here but by adding this motion value on to their coordinates you're telling them no you should be here instead so you distort the image but not only that remember our motion texture is actually moving because we added the time onto it which means you not only distort it you make the distortions move as well so let's add the motion to our input here and see what happens there we go you have a rippling wavy effect on our texture so final texture isn't actually moving at all it's getting distorted but its distortions are moving so it looks wavy and dynamic and it looks cool if you wanted to change the direction that it ripples in you can change the motion vectors here make the multiplier of the time negative 0.5 instead of 0.5 and as you can see it flows in the other direction of course we're just adding time times 0.5 the same value to both the X&Y so it's either positive time for both x and y or negative time for both x and y so it only flows towards at the top left and the bottom right towards 0 0 and 1 1 if you wanted it to go into other directions you can manipulate the X and y values of the coordinates separately let's say let's make a new vector 2 here and instead of having the same value for both of them we can have vector 2 with time x negative 0.5 and time x 0.5 and as you can see it moves in that direction now so now that we've got the fog let's make it at the purple color again and as you can see I feel like it's a bit too strong right now so down here when you're declaring the color instead of just putting final as the Alpha you can have a multiplier of say 0.5 and it'll be less opaque now so let's run our game and as you can see we have a dynamic fog that's flowing in the foreground you can obviously change around the color with this vector 3 value here I decided to go for more of a brighter bluish color instead of the dark purple just to give it a bit more contrast but the important thing is always always always remember to press save on your shader file and that's the only way to ensure that you don't lose your shader file and all the work that you put into it so yeah that's it thank you so much for making this fun to the video I'm really happy that people are enjoying my content again if you found this video useful please leave it at like I'd really appreciate that and if you want to see more subsidy channel and turn that notification bill on what's the toy would you like me to make next tell me in the comments but other than that be sure to check out the book of shaders calm it was a really useful website and I'll see you next time [Music] you [Music]
Info
Channel: Gonkee [OLD]
Views: 64,174
Rating: undefined out of 5
Keywords: gonk, gonkmakesvideos, gonk makes videos, blender, blender 3d, cycles, render, 3d, tutorial, blender cycles, godot, godot 3, godot 2, godot 4, godot shader, fog, animated, dynamic, cloud, noise, texture, make, game, procedural, unity, unreal, glsl, opengl, directx, cryengine, gonkee
Id: QEaTsz_0o44
Channel Id: undefined
Length: 24min 0sec (1440 seconds)
Published: Fri Oct 05 2018
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.