>> 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.