Building advanced effects in Niagara | Unreal Engine

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
>> Hello, everyone! My name is Chris Murphy, and I'm an Unreal Engine Evangelist. And today we're going to be taking a tour through Niagara, which is Unreal Engine's latest visual effects system. It's a pretty powerful tool and honestly, it's actually been in the editor for a little while, but it's previously been considered early access. And it's only now that it's considered something that is for general consumption and product release. So because we're going into production with this, it's a good idea to start kind of setting out how things work, how it's all set up, and how you can get started with Niagara. Now, to begin with, I'm going to go ahead and go to my content browser. I'm going to go to Content. And I'm going to right click. And I'm going to go to FX. Now, I know I haven't done much, but this is actually where a lot of people kind of make their first slip up. And what I mean by that is there's a lot there. There's dynamic input scripts. There's effect types, emitters, functions, modules, parameters, the system itself. And the truth is, if you're just learning Niagara for the first time, ignore all of these just for the time being, and just focus on a Niagara system. Now, I'm going to open up the Niagara system. And I'm going to make this one that's from selected emitters. Now, this is going to make a little bit more sense once I go into it. I could be making them from templates and using things like simple explosion bases and things like that. But the truth is, for demonstrating it, I feel like it's a little bit easier for me to start with something a bit cleaner. So I'm going to create a new system that's empty using an empty emitter. And I'm just going to add that. And I'm going to click Finish. And I'm going to call this NS_spinningparticles. Now, with NS_spinningparticles, this is what we see. So this here is the emitter. And in theory, I could add as many of these as I wanted. So if I wanted to kind of copy that and have another one, I could have multiple emitters stacked up. I'm not going to be doing that for this presentation, though. Now, how it works is this. Every single one of these sections executes the modules that are beneath it. So when the emitter spawns, it would execute code that would be there. When it's updated, it's going to have stuff here, and every particle when they're spawned will execute this. And every particle, every tick will update and execute this. So let's go ahead and add some and see what it does. I'm going to go to my emitter update. And I'm going to add one called spawn rate. Now, spawn rate is pretty straightforward. I'm just going to tell it to spawn 400 particles. And you'll notice that we get one very bright dot in the middle. And this is spawning 400 per second. Now, the reason that we're seeing one giant, bright dot is because what we're actually seeing right now is every single particle in the exact same location. So watch what happens if I go to my particle spawn and add a module to this group. I'm going to add, just for demonstration, a sphere location. And we can now see that this particle system is actually spawning those dots in a sphere at that spot. And that's great. So let's have a look at what particle update would do. So let's say I wanted to add some force to it. I could, for instance, add a vortex force. Now, when I added that, we see an issue on the right hand side. Now, the issue that's popped up is that the module has unmet dependencies. So what's actually happening here is we're adding the vortex force, but that force isn't being applied, because Niagara lets you kind of stack all of these forces together, and then at the very end kind of add them to the final position. So what actually needs to happen is this solve forces and velocity module needs to be placed lower than my vertex force. Now, I could just click fix issue. And we'll do our best job to kind of fit it. Now, the next thing that I'm going to do is I'm going to add a gravity force. Once again, I'm just going to put this above my solve forces. And we can see that when I add the gravity force, those particles are just falling. Now, I don't really want the gravity to kick in that much. So let's just have just a little bit so over time they kind of slowly make their way down, nothing too fancy. And the final thing that I'm going to do here is I'm going to add a point attraction force. Now, point attraction force can just pull things in towards it. So if I was to say a 400 meter radius and I want to have an attraction force of, say, 5, we can now see that some of these particles are kind of pulling their way back in. Now, at any point, I could look at this, and I could get it. And I could just drop this into the world. And we could actually see the system live here. Now, all right. This is kind of cool. Nothing too fancy, but it's worth noting, though, that everything I've done so far isn't really anything that's outside of what you could have achieved in Unreal's existing system, Cascade. So let's do something a little bit more interesting. This time around I'm going to add a color node. Now, a color node seems straightforward. I can select here. I can give it a color. It is now that color. But what's great with a color node, or any of these nodes really is that any input you can actually click on this little arrow and add what's called a dynamic input. So what that means is if I wanted, I could do something like I could draw this color from a curve. Now, let's say I want the particles to start white. OK? And then after a little bit of their life I want them to then turn red. You can now see that at some point in their life, they're then turning red. And now let's say that I wanted, after they turn red, they want to stay red for a while. And then I want them to turn green. Let's do a really vibrant green as well. Now, if I go back into the world, we can see that they start red. And after a bit of time, they eventually come out of this. Now, this isn't too amazing. Don't get me wrong. But what's important to note when it comes to this is that this is pulling its information from normalized age, which means that when it's at the end of its life, it has a value of 1, and it's referencing this part of the color. And when it's at the beginning of its life, it's got a value of 0, and it's referencing this part of the color. Now, the reason that's important is that, once again, this value here, I could run my own dynamic input. For instance, maybe instead of using the normalized age, we want to use the distance as a normalized range. So I could actually say, hey, get me the distance between the particle's position and the simulation's position, and tell it that a value of 400 is a value of 1. What you're actually going to see is that only when they reach those specific markers do they start to change color again. So see, when we hit here, it starts to switch over. And that's kind of where things start to get interesting with Niagara, because it lets you essentially move data around and rebind things wherever you want. It gives a lot more control to artists without having to actually have a programmer on board to kind of engineer some of these things. And that's incredibly powerful. Now, the final thing I'm going to do while I'm on this emitter is I'm going to go down to this section that says, render. And I'm going to point out that we actually can add extra renderers. So with the sprite renderer, we're rendering little dots in space. OK? But what I actually want to do here is I would love to render a light so that every single particle is pulling the same information at the end, but now these particles are kind of having their own little rave out here. So this is working well, but I want to put this particle system over here, and it's time for me to start doing something a little bit more interesting. So let's go look for Crunch. Here he is. So Crunch is kind of stuck in this area. We have a cage all around him. So what I would love to do is I would love to use Niagara combined with the material editor in Unreal Engine to actually go ahead and make him dissolve through the fence so that he can turn into little nanoparticles whenever he reaches the fence and then reform on the other side. So I think that effect is going to be reasonably straightforward. So let's go ahead and punch that out now. I'm going to close down my spinning particles, and I'm going to create another effect. Once again, it's a Niagara system. It's going to be using a standard boilerplate empty emitter to start with. And I'm going to call this NS_dissolveeffects. Now, for NS_dissolveeffects what I need to do is start the same way. I need to add a spawn rate. And my spawn rate is going to have a value of something a bit higher this time. Let's do 80,000. Now, I've set it to 80,000 and you're probably like, but Chris, my performance. Why? And yeah. There could be some performance issue there. But we can get around that pretty quickly by just instead of having this on the CPU, setting it to be on the GPU. And when we set it to be on the GPU, I'm also going to give it fixed bounds. Now, I'm not really going to get into this, suffice to say that when an emitter exists on the GPU the Engine doesn't actually know how big the emitter is. So what you need to do is basically hard code in the rough size of that so that it doesn't cull at inappropriate times. Anyway, that's now set to be a GPU emitter. But this time, instead of using a sphere as the particle spawn, what I'm going to do is I'm going to call reproduction. And you'll see that we actually have a great node here called initialize mesh reproduction. And if I check that, and then I set the preview mesh to Crunch, that's actually recreating that entire mesh by covering it in all of these particles. And this is really valuable stuff, because now that we're doing this, we can start to create something interesting. So let's take a look at what that actually looks like in the world. Going to hit Save. And I'm going to go to this character. And I'm going to add a component which is going to be a Niagara particle system. And I'm going to set this to be the dissolve effect that we just created. And you'll see that the Engine actually autofills any of those mesh references with the mesh that it's applied to if it's a component of it. But here's the problem. I'm going to hit Play. And let's look at what it does. Not ideal. But what are we actually seeing here? Well, I'll tell you. The velocity of each of these particles is being maintained from its spawn position. So if its arm was moving up in the animation at the time, that particle will just keep flying up. Now, fortunately, we have a great way of maintaining it. And that is that we can just type Update and add an Update Mesh Reproduction to our particle update loop. And again, I'm going to select this. Now, for anyone that's following at home, it's worth noting that if you have any issues at this point, it's potentially because you may be getting an error that says that this mesh needs CPU access. And there should be a little checkbox for you to check that just says, Enable CPU Access On the Mesh. Anyway, now that we have this going on, let's look at what's happening. If I hit Play, we can now see that those particles are actually adhering to the surface of Crunch. Great. So the next thing for me to do is to get Crunch and get these particles actually properly reproducing the aesthetic of Crunch. So we need them to actually have the same texture as the model that's beneath them. And that's actually pretty easy too. It just involves us creating our own material. And I could right click and create my own this way, but what I think might be a better idea for me to do is to instead just grab the existing Crunch material and use that as a reference. So I'm going to get that, and I'm going to duplicate it. And I'm just going to call it Crunch_Niagra_mat. Now, Crunch_Niagra_mat, I'm just going to pull an extra node. So this is the material that we have setup for Crunch. It's very straightforward for this asset. And I'm just going to connect this here. So what this material function does is it gets the particle mesh UVs of the specific area of Crunch that we're looking at. And it tells these textures to show that part of Crunch. Now, the next thing that I'm going to do is this section down here that says modulate mesh normals. Now, all I need to do is multiply the normal by this value. I'm just going to plug that in. OK? And now, this would get us mostly setup, but we are going to have a couple of issues here. So I'm going to hit Apply. And I'll show you what those issues are. And then we'll come back and we'll correct them. So I've hit Apply. And now, inside of here, set that to Crunch. I'm going to go to my sprite. And I'm going to set this to be the Crunch Niagara material that we just created. And we can see that it's compiling a Niagara version for the first time. So we'll give it just a minute to continue. Now, here's the first couple of issues that we can see. First off, they're all squares. And I would like to fix that. Next off, they're not actually adhering to the angle of the character that we're looking at. Those squares are always facing us. Now, fortunately, that's actually pretty easy for us to solve. All I need to do is click on the sprite renderer and tell the alignment to be custom, since it's automatically being set by Update Mesh, and to set the facing mode to custom as well. And you'll see that they're now actually wrapping themselves around correctly. Cool. So with that done, the next thing that I want to do is I want to start getting these to no longer be squared. Fortunately, I can add a node to the material editor called Radial Gradient Exponential. And if we look at that, we can see that it's just a white circle that fades to black at the edges. And that's great, because what we're going to be doing is we're going to be getting that. And I'm going to do a little technique that I like to do, which is multiplying the value by 2 and then subtracting a texture from it. And that's going to give me a bit of a noisy edge. And I'll show you what that looks like. So the existing version that we have when it's just a radial looks like that. But when we subtract the edges from it, we end up with something that looks like that. Now, I'm going to get this value, and I'm going to multiply it against particle color. So for anyone that's unfamiliar with it, particle color is a way of feeding in the color data from a Niagara or a Cascade system into a material. So I'm just going to say, if you modify the alpha channel, modify that. At which point, I'm going to saturate it, which is clamping it between 0 and 1, because I never want to go above 1 or below 0. And then I'm going to get this. And I'm going to add a dither, temporal AA. This is a nice, little technique that means if you have a masked asset it will actually blend in the mask areas without using translucency. Nice, little tip, but it works pretty well. So I've plugged this all in. And the last thing to do is just plug the emissive color into that. OK? Now, I'm going to hit Apply. And we're going to go back to Crunch and look at what he looks like back in the dissolve effect. All right. So that may not look like it's working, but it actually is. The trick is that this initialize particle node is setting the default color to white. And I'm just going to turn that back down to black so that it's the underlying diffuse. And that's working. We've now got the character able to be reproduced in particles. So this is good. This is fine. This is working. But what I would like to do is one other thing on this, which is I'm going to set this particle to be two sided. Now, with this character setup and all of the particles set to two sided so that they're correctly rendering, the next thing that I would like to do is I would like to set this up so that the underlying material for Crunch goes invisible whenever it touches a wall. Cause what we're going to do is make Crunch go invisible when he touches a wall and make the particles around the invisible area scatter and look like they're the bits that are flying around. And that's actually pretty easy to do. All I need to do is open up Crunch's material. And we're going to access something called distance to nearest surface. Now, distance to nearest surface is a really useful little node. But it's worth noting that it's only available if you have distance fields enabled. So you need to have generate mesh distance fields enabled. Now, I've got this set up. And what I'm going to do is I'm going to get the distance to the nearest surface. But I want to fade depending on the distance to the nearest surface. And I want to have a band around me that stays a certain color before it fades. So it kind of stays black before it fades to white. So to do that's actually pretty straightforward. Let's say you have something that is 50 units away from you, and you want to have a 30 centimeter band of the same color and a 20 centimeter distance fade. All you actually would need to do is subtract 30, and then divide it by 20. Because 50 minus 30 is going to be 20. And then 20 divided by 20 is going to be a value of 1. So that basic math there is a really useful design pattern. I use it really frequently. But let's look at what we're doing here. I'm going to subtract 20. I'm going to divide it by 30. I'm going to hit Q to align the nodes. And then what I'm going do is I'm going to saturate it so that nothing is below 0 or above 1. And then I'm going to run this node called dither temporal AA. And we're going to do the exact same as before where I plug it into the mask. I'm going to set this to be a masked particle. And now, if I hit Apply, let's go look at what Crunch now looks like in the world once this shader has finished compiling. Let's bring this character over here. Really quick look. OK. Here we are. So we can see here that this character has faded out around those edges. Got a nice range there. And the feet actually were. We just had a lot of particles that were doing it. So we now know that that part's invisible. The next step is for us to get these particles to separate to finish the effect. And that'll tie everything together. I promise. So let's see what that actually looks like. Well, if I hit Play, he'll run into it. We can see that he's fading. So I now need to figure out, how do I do that material logic in the Niagara system? And that's actually reasonably straightforward. What I'm going to do is I'm going to go to my dissolve effect. And I need to create a way to blend between a physics crazy version and where the particles need to be, which means I need to have some sort of variable that I'm keeping track of that is going to be considered the current dissolve amount. So I'm going to go to my particle update. And I'm going to create what is called a New Scratchpad Module. And I'm going to call this Get Level of Dissolve. Now, if I go back to the system, you can see it's actually created a module for us called Get Level of Dissolve. Now, what Scratchpad is is it's basically a way of creating modules or dynamic inputs that only exist in this system. They're not assets that you save and share between everything. They're a great way to just kind of try things out, rough stuff up. And it's really, really useful and incredibly powerful in a tech artist's utility belt. So I've got this. And I'm going to now create some things. So the first thing I need to know is the distance to the surface. And fortunately, I can do that by creating a new collision query. So I'm going to get this, and I'm going to call it query mesh distance field GPU. And you'll see that this function is actually very similar. So I'm just going to pull that across. This function is actually very similar to the one that we had in the material. But it does need to ask us a question. It needs to say, where in the world are we sampling? And that's actually pretty straightforward. I can add a vector. And I'm just going to call this sample position. Now, a great little thing that we can do here is whenever you add an input, you can actually tell it to bind to a value. So if the developer doesn't actually set sample position, it will automatically be the particle's current position in space. So now that we've got that, we're going to follow the exact same logic that we had before. I'm going to subtract a number. And then I'm going to divide a number. So let's set this up. And I'm going to add create new parameter. I'm going to create a float. And I'm going to call this bias. And I'm going to add another float. And I'm going to call this one distance. Wonderful. So we're now subtracting that and dividing it by that. So with that in mind, all I need to do is what I did before, which is to clamp them so that we don't go above 1 or below 0. And the last thing I'm actually going to do here is I'm going to flip it. So at the moment, this goes from 0 to 1. But I really want my dissolve amount to be a value of 1 and my undissolve amount to be a value of 0. So I'm going to plug that in as a 1 minus. And I'm going to create a new output that is going to be a float. And it's going to be called Blend-- Oh, how about dissolve amount? Now, one thing that's worth noting is that every time you create a parameter, it exists in a certain namespace. And that is that some things exist only in the particle space, in the output space, and transients. In this situation, we want to make sure that this information that we're setting is an output, which means that it's going to be something that other modules can actually access the information that this created. So I've now got this. And I'm going to set the same values as before of 20 and 30. So 20 and 30. You'll see sometimes that when you first create a collision query, it has a script value. I'm just going to click that, which will automatically reset it. And in theory, that should be all we need for the level of dissolve. So if I was to go to this now, I now need to blend the values. So how do we do that? Well, the first thing I'm going to do is quickly correct something. The moment whenever we have this solve forces in velocity, its information is essentially getting overridden by this update mesh reproduction. So I'm going to disable this value called write to intrinsic properties and just keep the mesh reproduction itself. Now, the next thing that I'm going to do is I'm going to create a blur, which will blend this information with this information according to this information. And that's really straightforward, because, first off, we need the alpha. And the alpha is the dissolve amount. And you'll see the value that we just created is available for us. Now, the next thing is the position. So we got to think to ourselves, well, OK. What positions do we want? When there's no dissolve, then we want the update mesh position. And when there is a dissolve, we want to get the information that we didn't apply, but we did still calculate from the solve forces and velocity node. Now, I'm going to do the same thing with the velocity down at the bottom. And that is, I'm going to say, hey, when the value is 0, I want you to be the update velocity. And when you are the velocity, I want you to have the solve forces velocity. And with those in place, I'm just going to add one tiny little thing to get a bit more chaos going on. And that is that I'm going to add the vortex velocity to all the way up here. Put it on a very low number. I'm thinking like 20. Cool. Now, I'm going to hit Compile and Save. Now, I'll give it just a second. Now, this may not look like it's doing much, but if you check out those feet, which are very close to the surface, they're dissolving into particles. And if I was to walk into here, we can see that this character is now splitting out into particles at that location. And if we focus on it, we can actually see that we can see through the character at the locations that he's pulling apart. So that's kind of cool. We're getting somewhere. The last little thing that I'd love to do to finish off this effect, though, is to once again add the color. So I'm going to get the color. And I'm going to add a curve. So the color from curve, once again, instead of using the normalized value, I'm going to do something different. I think it'd be great if we looked at the dissolve amount and we told these particles that when you are 0% dissolved, I want you to be black. So I want you to be completely invisible. And I want your emissive to be black as well. As you start to dissolve and fade away, I still want it to be black, but at some point I would love for these particles to appear. And then let's flash them a color. Let's go a really bright green. And I'm then going to fade back to white at a distance. You know, I might even fade back to black. That could look cool. Because I think that will get them kind of emissive. If We want to see how that's looking, I can get this character. I'm going to pull him across. We can see here that those particles that are intersecting are kind of doing that. Let's see what that looks like in motion. I may have gone a bit overboard on the brightness value there. So I might just dial that back just a little bit. I'm going to bring this back to here really quick so we just get a narrow band of it. Let's see how that looks. There you go. And there it is. Now we kind of get that ghostly green at the merge point, but everything else is still trying to form itself. And that's all it takes to kind of get something that you've never made before in Niagara that you couldn't achieve in Cascade. So I hope this has been useful for everyone. I hope this has been a powerful session. And I really hope that everyone kind of looks at this and continues to make some really outstanding stuff in Niagara with Unreal Engine 4.25 and beyond. Thank you very much for your time.
Info
Channel: Unreal Engine
Views: 174,484
Rating: 4.9574118 out of 5
Keywords: Unreal Engine, Epic Games, UE4, Unreal, Game Engine, Game Dev, Game Development
Id: syVSRDQxrZU
Channel Id: undefined
Length: 28min 54sec (1734 seconds)
Published: Wed May 27 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.