ARRAN LANGMEAD: Hello, everyone. Welcome to GDC 2023 here
at the Epic Games booth. My name is Arran Langmead. I'm a senior content
developer here at Epic. And today I'm going to be taking you
through the amazing new procedural tools that we've got in 5.2. If you saw the State of Unreal
talk that happened yesterday, then you'll have seen
the amazing Opal demo that we showcased,
which is this lush forest environment with that amazing
car driving through it. And much of that was built
using the tools that I'm going to be showing you here today. So with that being said, let's
just dive straight into the engine. Now what I've got set up here
is the start of my game world. Right? Don't tell anyone at Epic, but
secretly, I'm going to take this, I'm going to leave them, I'm going
to make all the money in the world. They have no idea, so please keep it
to yourself until the game launches. But I've just started out. It's my two kilometer by
two kilometer landscape. And I need to decorate it. I've just started out building it. I've got my landscape down. And really I need to start
populating it with data. So to do that, I'm going to use PCG. PCG stands for procedural
content generation. And I'm going to start off with
my rock volume that I've created. And I just want to show
you this because it just shows how quick this tool
is and how powerful it is. So the volume represents the
entire two kilometer landscape. I'm going to press
Generate, and boom. In a couple of seconds, it has
decorated the entire two kilometers with rock data. So all of my cliffs,
all of my rocks, done for the entire
two kilometers. And I can do this again. I'm going to-- I am. Just going to get rid of it all. There it goes. None of this is cached. Right? It's just completely generated every
time I press that Generate button and it creates all
of that data for me. And actually, because I'm working
in this kind of placeholder mode where I'm just starting out making
my game-- secret, don't tell anyone. I only actually have one
rock asset to use for this. So this is one rock that I'm
placing just absolutely everywhere. But the great thing about this
tool is because it's procedural, as soon as I have more
rocks, as soon as those lazy artists
that I haven't employed yet start to do some
stuff, they can just add them in, and we can start
adding that data in. Same with my forests. I need some forests
in my environment. I've actually painted down some
areas where I want my forests to be. These little brown spots here. I click on my forest demo. I press generate. Give it a few seconds because this
one is actually significantly more dense than the rocks are. But even that's still pretty quick. We have a look over here. Still generating. I go down to my forest floor. And you can see I've got all
of my trees, all of my bushes, all of my ground data,
all generated in what was that-- six seconds, maybe? And that's my entire scene. I'm good to go. Ship it. Done. No, we're not ready yet. But how does this tool work? So the way that this system works
is that we have a new graph, which is our PCG editor. I can show you it if
we go to our graph. Let's open it up and click on this. This is how you build out
your procedural toolset. It's super easy to use. If you're feeling a bit intimidated
by seeing this graph, don't worry. We're going to run through it all,
and you'll be running in no time. So it starts off with our
input, where we take in data. This is where we decide
what type of things we want to sample,
what type of things we want to look at
for our particular in. In this instance,
we want the landscape. Then we sample it. So we generate a load of points. Then we filter that data. So we modify it, we tweak it,
we check against, let's say, where our input source is. Something like forest. And we filter that data out. And then finally,
we output the result. So we have our static meshes
that we want to output. And that is the bulk of what
we're going to be covering today and the bulk of what you'll be
doing with a lot of your PCG tools. Really simple. So just hold on to that. Whenever you see an
overwhelming graph, just remember, we take in data,
we generate points, we filter it, and we output a result. And where
we're looking at today is landscape, and we're looking at
generating meshes. That's not all we can do with PCG. We can generate all of this to
generate Blueprints, lights, Niagara particles,
sounds, whatever we want, we can use this tool
to output a result for. So let's go ahead and
build a PCG live on stage. What could possibly go wrong? I've got a nice little area here,
which is my nice open plane. I want to put some grass down,
some shrubs, a little bit of foliage just in this environment. Now I'm going to start off
just to make sure that you have everything to get you started. You need to make sure you've
got the Procedural Content Generation Framework plugin
enabled, restart your editor. I've already done it,
so I don't need to do so. And that's going to give you
three main things that you will have to work with. It does give you more than
that, but we're just going to be focusing on three. The first is if we go to our Place
Actors panel and type in 'pcg', we get a PCG volume,
which we can place into our world. If we click on any actor
inside of our scene, or if we make a Blueprint we
want to add a component to, we can add a PCG component to
any actor inside our scene. And what's great about this is
that the PCG will automatically pick up whatever it's been added
to, and then we can sample whatever we have. So if I have a spline in my scene,
I can add a PCG component to it, and it will automatically
pick up that spline for me. I don't have to do
any manual linking. Just as soon as I add the component,
it's all there and it's all done. The final thing that we get
is inside our content drawer. If I right click, we have a new
section called PCG and PCG Graph. That allows us to make
our graphs that we use for all our procedural generation tools. So we're going to start by
making our nice PCG environment. I've got this nice open space here. I'm going to go click
on PCG_OpenPlane. And let's find our graph. And I'm going to clear this, and
we're going to make a brand new one. So let's call this-- put it in the right folder. For I shout at myself. And we're going to
call this 'PCG_GDCDemo' and see if that breaks. It doesn't, which is great. OK, so we open that up
and we've got our in. Now I'm going to take
this output here, and I'm just going to
move this a little bit out the way right over
here, and then I'm going to move over like
it was never there. Because we actually don't need
it for this particular demo. So we're going to take this. Now starting off with our
inputs, if I expand this tab, you'll see that we have a
load of different options. Now depending on how
you set up your PCG, these input options will
give you the same thing. So back over to our
demo, if I close this, you can see that under Input
Type, I've got it selected as Landscape here. That means that even though
we've generated a volume, we don't want to use
just the volume, we want to use the landscape
that sits within that volume. If I change this to
Actor, then we'd actually sample within the entire volume
space regardless of the landscape. But because I've got it
set to Landscape here, that's going to be the
default input that we have. So actually, my in, my input,
probably my actor, landscape, and landscape height are all
going to give me the same result. So it doesn't really
matter what we're using for this particular thing. So I'm just going to start with
in, and we're going to start by
sampling our input data. This is going to be
our point generation. And all our points do is they
are like a transform in space, and then they hold some
other data as well. We can add data to it. We can set extra stuff, and we
can randomize based on that data. But this is our base
thing to work with. So I'm going to press Save,
I'm going to go to my world, and I'm going to press
Generate, and nothing happens. And that is actually intentional. Because while we've
generated a load of points, we haven't told it to do
anything with those points. There's nothing physical
for us to render. And this is where
the tech artist brain that I have inside my
head lights up, because we have the most awesome
debugging tools for this that I've seen in ages. So on the bottom right, you'll see
here we've got our debug section. This allows us-- I'm
going to press D, and I'm going to move this out
the way, just so we can see it. Go back to our thing. This allows us to preview all
of the points that have been generated for this particular PCG. And I just zoom out a little
bit so we can see it a bit more. And that's going to let us visually
see exactly what this PCG is doing. And we can step through every
single node that we generate here and see all of the changes that
are happening at the debug level while we're working. Now this surface sampler has a
few parameters that we can edit. We've got Points Per Squared Meter,
Point Extents, and Looseness. Now something to keep in
mind, these three values, they are interconnected. So when you make changes to
one, it can be limited by or impacted by the
other values that you have set. And as example of this, I'm going
to take my Points Per Squared Meter, and I'm going to increase it,
and you'll see that nothing happens. And that is because we're at
the maximum number of points per square meter that we can fit
into this environment already. If I were to take my Point
Extents and reduce it, so make these boxes
smaller, like so, then suddenly I have a much higher
density, which I can then control by Points Per Squared Meter. You can see I can start
reducing that value down to control how we're
spawning those assets in. Our other value that we have
as well is Looseness as well, which if you have a look at this,
I'm going to go kind of top down. You should be able to see there
in a bit of a grid pattern. And if I turn this down,
you'll see that the grid pattern becomes even more pronounced. You can see that
that surface sampler, it's taken that square of landscape,
and it's put down a grid of points, but they're very uniform,
they're very even. And what looseness
aims to do is just add a bit of
randomization in there. So as I increase this
value, it's actually increasing the spacing
between all of those points and then moving the position
of those points just a little bit to give us some
nice random positioning. So I'm going to set
that back up to one because I want it to
be nice and loose. Keeping it easy. And then we're going to
do some more work here. So we've got our points. Now an interesting
one about this as well is that our bounds of our points
is a really useful element of this that you need to keep in mind. These squares that
we're using here, they are actually a physical
representation of the space as well. So as well as having
a point in space, we also have a volume of that
space, and we can use that in a number of ways-- mainly filtering out other data. So if we can check if two bounds
are overlapping or conflicting, we can use that to remove it. And that comes in really handy when
we're doing procedural generation, because we don't want our rock
spawning inside of a tree. We want to avoid
that where possible. So we can use these bounds
to actually control and limit what we're spawning and where. So our surface sampler is
our base that we've got. We've generated our points. Now we want to filter that data
out, because we don't want to spawn an asset on
every single one of these points. We might do in some instances,
but I don't for this example. So in order to remove
those, I'm going to use what's called
a Density Filter. And the Density
Filter is attached to the density attribute,
which is just a pre-assigned value that we have
on every single one of our assets, and you can actually see it because
it's our gradient for our cube. So you can see that value
between zero and one. That's our density
value representation that we're getting to see here. Now if I switch my debug
over, I'm going to press D to get
rid of that one and press D to turn
it on on that one. You'll notice that our
values between 0 and .5 have been removed. And that is our density value
stripping out those points where they're no longer needed. Now we can switch this round. We can play with these values, and
you can see that all of that stuff is updating in real time so that
we can really art direct and tweak and change these values to
get the right kind of density, the right kind of shape
of all of the points that we're generating in our scene. Next up, we want to make
it even more dynamic. so I'm going to add what's
called a Transform Points node. And what that will do is
that's going to give me a range between min and max for my
location, my rotation, and my scale. So I can take these points,
and I can say, OK, actually, you're all too
uniform at the moment. I want my scale range
between one and two, and I want my rotation range
to be between zero and 360. And that's going to immediately
just give me a load more transform variation on this particular asset. Now one thing I want to draw
your attention to as well is you'll notice that in this,
they're aligned to the normal. That's because we
sampled the surface of the landscape, which gave us the normal and it aligned it to it. But in some instances,
you might not want that. If you've got a tree,
you don't want it when it spawns on an
angled cliff for that tree to be coming out at that angle. You want it growing
up like a normal tree. So in order to fix that,
we can use Absolute Rotation, which resets the rotation on it. And then we can assign some random
variation on top of that as well. I'm happy with these to be
aligned to the landscape, so I'm just going to
leave that as normal. And then finally,
once we've got our points, we can actually start
rendering stuff. So in order to do that, I need
to choose what I need to render. Now for this, I'm going to use the
StaticMeshSpawner, which actually is going to be putting down
instanced static meshes, not just static meshes. And that's going to give
me an array of meshes that I can put into my scene. So I'm going to get rid
of the debug on this, go back to my original asset, and
I'm going to put in my grass mesh that I have. And you can see
again, straight away, that puts the mesh straight in, no problem. So we can render all of
that nice and easily. Then I can add some more. So let's add another grass tuft. So to here, grass, add a tuft, and
then we can see that update on there as well. And let's add one more. Let's go in. Let's add my shrub. Yay, there we go. So now we've got all our stuff. But that's way too many shrubs. We don't want that in our scene. It's going to be way too
overwhelming for the player. They like grass. They don't like shrubs. We've done that market research. We know it's a thing. So we're going to reduce
down the number of shrubs that we have in our scene. But how do we do that? That's where we have different
ways of actually managing when we choose what static mesh
for this particular environment. Now on the top of this, you'll
see we have a Mesh Selector Type. By default,
it's going to be set to weighted. But we have three predefined ones
that you can use in here already. And actually,
we can use this to build our own. So if we wanted to,
we could build our own logic to select how we choose what
mesh to put on what asset. Now I'm going to leave
this as weighted for now. And what that will do is
it gives me a weighting option for all of my meshes. And as I increase that
value, it's going to choose that mesh more, x more
than the other meshes in my scene. So if I go to this
one, I don't want 51. Let's do 15. That basically chooses
my grass mesh a lot more, meaning that I've got a much
more reasonable number of shrubs in my scene,
my art director is happy, and we can move on with our asset. But before we do that, I want
to show you how live this is. Because we are
updating this as we go. So if I actually want my scale
to change, I could go in now and I can update it,
increase the scale. Or I could decrease it,
I could get rid of my rotation, I can do pretty much
whatever I want to this, and it's all just
going to update live. The last thing I wanted
to show you towards, which is again, shows off
more of my tech art nerdiness, is our attributes. So when we sample this data
here, the landscape, we're actually getting more than
just the location, rotation, scale, bounds, and the density node. We're getting extra
data on top of that as well that we don't
necessarily see when we're just using the default debug mode. If I press I on any of these
nodes, that's going to let us inspect that node. I for inspect. And then if I select
a partition type, you can see that we get this
lovely, lovely graph that has every single attribute and point
listed inside this partition square. So you can see we get our transform,
we get our bounds, we get our color, we got our density and steepness,
which again, we didn't necessarily know we had. But also we have the weight
data for the landscape as well. Because we sampled the
landscape, we have all of the weight data
for all of those points. So again, we can use that to filter
out our information even more really easily. One other thing as well,
you'll notice that we're talking about partition here. It also handles partition
for you as well. So if I have my scene
here, we go down, we find what's called
the PCGWorldActor. I open this up, and you'll see
all of these partition cells. And what it's done is when
we generate data with PCG, it actually takes
all of the instanced static meshes and groups them together into your
partition squares. So if I click on these,
you'll see that all of these have been batched into a logical
partition square, which we can then update, and it will again propagate
all of that information through, which just makes
streaming so much easier. Because again, all of that data
is just managed and sorted for us, and we don't need to worry about it. Last one I want to show you is also
my snow, just because I built it. Again, when we're working
with the landscape, we often have quite
a low resolution. Generally when you import in,
unless you're scaling it down, you're working to
one pixel per meter. And this is a little bit-- a bit blobby. You can kind of tell it's
snow, but not really. So I'm going to go to my snow demo,
press generate on my scene, boom. There we go. And you can see that
we get a load of extra snow mesh
data just populated on top of there,
which just adds to on top of it. Now let's add a little bit
more complexity into our scene. So we have a spline here
that represents my river. So I've got this lovely little
stream running through my project. But I want to decorate that
stream with some assets. So to do that, I've actually added
a PCG component to this spline. And then I can click on that,
goes to generate just like anything else, and boom, it runs along
the entire spline and generates all of that data for me. You can see now,
I've got my lovely Nanite pebbles, I've got my rocks along the edge,
I've got some nice bigger boulders in here, I've got some little
tufts of reeds sticking out, again, along the entire
length of this river. And this is a long river that
I've got in here as well. You can see all of
that data in there. So how do we build this? Let's take a look at the PCG. Now it's a bit bigger. But remember, remember what I said. All it is is taking an input,
generating sample data, filtering it, and outputting a final
result. So in order to do this, we need a different sample,
which is called the spline sampler. So let's again go side by side
so we can have a look at that. So when I sample my spline,
I get points generated all along the length of that spline. You can see that I've got my
Mode set to Distance over here in the top right,
and that lets me set the increment amount, so every 100
units, I'm going in and I'm adding another point. I can also set that
to Subdivision as well if I want to have it as more of
a division over the spline rather than an even distribution. And then once I've got those points,
they're not particularly helpful right now, because I don't
want to make a giant snake, I guess, out of meshes. I want to have these
nicely scattered. So to fix that,
I can use the Transform Points node to do a min and max
in my location, which is going to spray those points out
in that direction that I choose. So -600, positive 600. Now once I've got those
points, they're looking great, but they are floating in
space, which isn't ideal. So what we want to do then
is project those back down. Now the projection
tool is super powerful. I'm using it here for the
landscape, which you can see I get Landscape Height. Plug that straight into
Projection, and that's going to allow me to snap all of
those points down to the landscape. But we can also just
do a raycast as well. So if we're working with
meshes rather than landscapes, it's just as easy for you
to do raycast in the world, choose the direction you
want that cast to go in, and then it will project
onto whatever surface that you happen to have
available in that scene. So my projections got those points
down, and that's all I need. I've got some density in there. I can do a little bit
more transformation just to give me some nice rotation
random and some scale random, and then I can render
out these meshes. So if I disable them just so you
can see each one side by side. Let's go in, get rid of those. There we go. We've got rid of it all and
let's bring it all back again. So we've got our little
pebbles coming in here. We've got the edges
of our rocks here, so just pushing that out
to the outer extents. And then we've got our
boulders in there as well. And then finally we have our reeds. Now our reeds are
slightly more complicated, because we actually want
these in little clusters. I want some slightly more
organic growth to this. So to do that,
I need to generate some data rather than sampling the data that
already exists within the world. To do this,
I have a CreatePointsGrid node, which is just going to let me create
some arbitrary points in space wherever I want them. Now for this, I'm actually
working in local space. I just want these at (0,0,0), because I'm going to move
them around later on. So if I debug this,
I can't necessarily see anything. And that's because it's
at (0,0,0) in the world. So I actually have
to go under my scene to see the points
that I've generated. Now we're not going to
use these specific points. We're just going to
generate a point shape, and then we're going to scatter
that throughout our scene. So I've got my general points,
but I don't want a grid of reeds. That would look really weird. So in order to make it a
little bit more organic, I'm going to do a
DistanceToDensity node, which is going to let me create
a nice gradient going out, mix that with some noise. Again, let's preview that one. So again, our density noise
lets us generate some noise data inside that density
value and then choose how we mix it in with the
previously assigned value. In this case,
I just want to multiply it in. And that's going to take that
nice gradient I've just set, and it's going to mix
it with that noise. Once I've got that, I can then
filter out the outer edges of those nodes,
those lighter nodes, leaving me with that circular interior
with a little bit of noise that just gives me some
nice randomization. So now I've got that. I just need to put those
points down inside my world. So let's go back
out and have a look. You should be able to see them here. They've all been projected down. Now we have a thing
called Copy Points. It lets me take in a source input. This is the thing I want to copy. And then to the target,
lets me choose where I want to put all of those points. Now I've already got
point data that I can use, which I generated from that spline
and that scatter in that projection. So I can take those points and then
copy my little cluster all over. Once I've done that, I can do some
more noise, some more filtering, just to change each
of those new clusters that I've got to give me
some more random variation. And then I can put down my
reeds inside my static mesh. And there we go. Hey presto,
we've got all of our reeds. So all of that being
said, I am pretty much out of time, which is a real shame,
because I love doing this stuff. The few takeaways I just
want you to keep in mind. I am only scratching the surface
of what this tool can do. You can sample so much more,
you can filter so much more, and you can spawn so much more
than what I've shown here. Other things keep in mind. These tools are experimental. If you use the engine,
then you know what that means when we say experimental. It means play with it,
have fun, try it out. Please, please,
please don't ship anything with it, because we haven't
finished making it yet. And then also just expect lots of
changes with that stuff as well. Other stuff to keep in mind. The stuff I've shown you
today is editor only, but we actually do have
game involvement as well. So you can actually
ship that empty level and then completely generate it
when the player loads the game. And then midway through that
game we could destroy it all and completely regenerate it again. It's incredibly powerful. I didn't show you-- again,
it's extendable with both Blueprint and with C++. And finally,
it's CPU-based at the moment, but we do have GPU still to come. If you do anything with
PCG, please post about it on our community channel. We love seeing this stuff,
and please give us as much feedback as possible. Thank you very much for listening. Have a great rest of your GDC. And I'll be over here if
you want to talk more. [APPLAUSE]