Procedural Content Generation in UE5 | GDC 2023

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
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]
Info
Channel: Unreal Engine
Views: 46,005
Rating: undefined out of 5
Keywords: Unreal Engine, Epic Games, UE4, Unreal, Game Engine, Game Dev, Game Development, Unreal Engine 5, GDC 2023, Game Developers Conference, State of Unreal, UE5.2, Procedural Content Generation
Id: aoCGLW53fZg
Channel Id: undefined
Length: 25min 35sec (1535 seconds)
Published: Mon Apr 17 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.