before we start let's discuss what procedural generation will do for our game design or more precisely what it won't do you've almost certainly heard how Minecraft is successful because it has infinite content respectfully I disagree with this I would say that Minecraft worlds have a finite amount of unique content because the only content it will generate is the content the algorithm is capable of generating the real value of procedural generation is rearrangement instead of perfecting how you play through one single map you get a shuffled map every single time forcing you to develop General strategies
and engage with the world differently at any point you might stumble onto valuable resources or a giant cave or some unexpected obstacle this way a randomly throwing a spanner in the work can promote inventive Solutions and negotiations and random rewards can create high points for the player I mean remember finding diamonds back in the day so that's the true purpose of procedural generation it's not a core driver of interesting gameplay but merely an assistant to promote the core Loops that do Drive interest in gameplay that said we still want to make some something fun to
discover with unexpected and awesome landscapes even if it's ultimately finite how can we get more out of our limited content the trick is to let features interact with each other since we know we're already working in a constrained system with finite content we can maximize the value we get out of that content by allowing all of it to interact and mix allowing interesting combinations to emerge naturally this is part of where I think Minecraft went wrong Minecraft takes a finite amount of pre-made content and restricts it to have specific carefully directed interactions with with other
terrain features leading to a fundamental predictability that makes Minecraft worlds look very syy so that's how I think about procedural Generation The Next Step naturally is to implement such a system I'll save the spicy details for the end of the video including the ultimate trick for handling chunk boundaries 100% correctly every time but right now I'm just going to focus on appearance to begin let's talk about terrain shaping the first stage of the process the goal here is to use noise functions like pein noise to create a basic shape out of voxal later on we'll
be able to decorate it with grass and carve into it with caves among other things the terrain shaping algorithm has three components Regional noise Continental noise and C compression Regional noise gives us the bulk of the visual interest above ground so I focused most of the noise computation on this step to start you can sample a density value using 3D pear and noise if the density is above zero you place a stone block and if it's below zero you place an AIR block that will give you a bunch of interesting 3D blobs there's a secret
source that makes this look like Hills if you decrease the density as the y-coordinate increases you encourage the blobs to be more dense at the bottom and less dense at the top this gives you a solid floor an empty sky and 3D variation in the middle you can tune the fall off to flatten or expand the terrain this is already okay but I wasn't entirely satisfied I still wanted more Cliffs but I didn't want the terrain to become too Blobby or froy so let's think about how Cliffs form in the real world Cliffs typically occur
at the boundary between Hard Rock and soft rock the soft rock is easier to erode but the hard rock is more persistent so a cliff is really an erosion discontinuity which is something we can model to analogize low value areas of our pein noise are easy to erode while high value areas of our pein noise are harder given all this we shouldn't be too surprised that we're not seeing Cliffs because it's uncommon for dramatic changes to appear in plin noise all on its own as the technical artists we need to introduce more discontinuities ourselves so
let's generate a second 3D P noise exactly the same as the first but with a different seed the algorithm will blend between the two based on a third 3D pein noise which has a soft and smooth contrast boost to create a fast shift from one noise map to the other and wouldn't you know it you can D this in to make some really nice Cliffs you might notice that I have a placeholder SE in some of these pictures yes they're literal water blocks I don't have a fluid system when I added a sea level I
found that lots of my Cliffs got submerged or crashed straight into the ocean creating Islands rather than those cool yosity style Cliffs to address this I gently push the terrain up and down it happens slowly over a large area to ensure each local region looks similar but at Continental scale it keeps the land and ocean at different elevations under the hood I sample a very large to the peand noise that's about 32 times larger than the regional noise this Continental noise is also softly contrasted just like the cliffs from before which keeps everything either well
above or well below sea level except along the small strip where the land actually transitions into the ocean at a large scale this Continental noise really gives the world a lot more structure but the other thing it does is add a lot of height in particular it makes the oceans super deep which is something that I wanted out of this terrain generator that said these oceans and transitional areas were still much too steep with no real beaches or changes in gradient it didn't look like the water was depositing any sediment so to speak which is
a bit unnatural looking to solve this I wanted to amplify the density ramp effect around sea level which would thicken the 3D noise into wide beaches this is where my C compression comes in I came up with a set of equations that can bend the Y coordinates down around sea level with a smooth increase in speed applying this to the density ramp you can see the terrain fatens out towards sea level this gives us plenty of space to paint on beaches later so with that we have a Terrain shape at some point we might add
more controls but for the time being this will do let's just take a look at the performance [Music] okay it's not actually that bad but given I have a good computer I would rather it be faster especially if we're just generating 16 cubed chunks The Slowdown comes from calculating our density function at all 4, 96 block positions inside of the chunk there's no Universe where doing that will be cheap the density doesn't actually change that much from block to block so sampling every block is quite wasteful instead I'll sample the density at just a few
points then join up those points with linear interpolation to fill out the whole Space cheaply this is called upsampling in our case I went with a four times upsample where the density function is cooled 125 times rather than 4,096 times even with the extra complexity of the upsampling this still runs 10 times faster so it's well worth it after the terrain shaping stage it's time to do top soiling but simply the top soil stage replaces the top layers of stone blocks with dirt for every stone block we start stepping up in single block increments trying
to find an AIR block if we don't find any air blocks within 12 blocks on top of us we leave the stone block alone because it's underground otherwise if we do find air we know that the block is close to the surface so it's a candidate for converting into dirt you could just do that and Minecraft does just do that but I wasn't happy with how this look the eclipse because Daydream has smaller voxels it's way more obvious when you have single Columns of dirt which looks really ugly so I look at the four adjacent
columns and try to find what elevation they're at if they're too far away or if they don't exist at all then that implies we're on a steep surface so we won't Place soil here as a bonus if we do find an adjacent column we can use it to measure how steep the slopers and reduce the thickness of the soil layer gradually to make the transition even cleaner to finish things off I convert everything below a certain elevation to sand to form some lovely beaches using some more pein noise I can even take some sand off
the top letting the beach separate from the mainlands to add some interesting elevation now it's time for caves probably the most important step step for our game games like Minecraft use pear and worms for this but I don't think they're a good fit here because dark linear corridors are really better suited for combat encounters which we're not doing and they aren't nearly as 3D and vertical as I'd like them to be what I really want is a 3D network of caves with tubular connections and distinct Rock faces imagine filling a jar with stones and consider
the shape of the empty space around the stones that's kind of what I'm going for something spongy and full of angles so I turned to wly Noise by generating a random infinite vorono diagram and using some distance ratios we can generate some pretty structured tubes and Cavities distort the input position with some polar noise and now you have some really cool cave shapes they're still highly 3D and have all sorts of fun angles and they retain their structure without being overly linear to break up the network a bit I alternate between two differently seeded borono
diagrams and to make deep caves more fun to explore I gradually add a constant factor to the noise to open up the caves as you go thousands of blocks down finally I generate two more vori diagrams to generate thin Runner caves which help connect the large caves to each other and create more cave entrances on the surface in the future I'd like to flood these caves with water or magma at various elevations and I do have algorithms in mind to achieve that but I'm not looking to implement that right now so having carved out our
caves it's time to grow some grass this is pretty simple really if there's air above dirt the dirt turns the grass by doing this after cave Generation The Cave caves can strip away layers of top soil without stripping away the grass which is key to ensure the caves don't Scar the landscape too much when you're above ground so finally we get to talk about trees in many ways these are the final boss of any chunk terrain generation system because they not only require lots of context to find valid spawn points but they also generate largely
across chunk boundaries I'll talk about how I did this in a bit but to make a long story short I built an abstraction for myself that lets me generate trees wherever I like as long as I set some boundaries on the reading writing throughout the chunk I test different positions to see if a tree can grow there the check is pretty simple trees must grow on grass and they must have empty space in a specific area above them to generate the trees I step up in single block increments placing logs in the middle to create
a trunk at randomized but regular points the code starts a branch off of the main chunk which steps outwards in semi- random directions and a little bit upwards too at the end of the trunk and the branches I generate a blob of leaves to form the canopy that pretty nice trees for visual interest I randomized the height of the trees between six and 20 blocks which gives them a nice amount of variance I also added a birch variant with some lovely yellow leaves which has a small chance of generating which breaks up all the green
and also added some fruits for some smaller pops of color in the future I'd like to do a more interesting competition system where different species of trees prefer different conditions and better adapted trees have more of a chance to generate than less adapted trees it doesn't have to be a full simulation some P noise would probably do to emulate different conditions but it help vary how different plants are distributed across the world so I'm really happy with how this terrain works I didn't set out to make the final terrain generation algorithm in one go but
this does start to meaningfully approach the kind of world I imagined this game having it's not done but it's not bad so the thing you're probably dying to know is how I dealt with those chunk boundaries because they're downright awful to deal with if you don't know what you're doing the core of the problem is that generating chunked worlds is inherently parallel any two chunks might be generated in different order ERS by different players in different threads in different world files on different computers at different times but they still need to fit together perfectly in
short we need our chunk generator to be deterministic that is it must always produce the same chunk no matter the external conditions and we need it to be parallel that is you can generate the entire thing start to finish at the same time as other chunks this is incredibly difficult to do in practice which is why almost Nobody Does it in practice even YouTubers don't do this and yes tantan I am humorously calling you out for coupling your world state to your terrain generator as you saw I divide my terrain generation into many stages and
often times these stages will need to read blocks from other chunks for example to figure out whether to replace a block with top soil you need to know if there's empty space in the chunk above as a naive workaround many people read from chunks in their live world state which is a terrible sin and you should never do that firstly waiting on hi traffic shed State can kill performance secondly any gameplay simulation that happens in the world can affect your terrain generator and alter the way things generate and thirdly you can create cyclic race conditions
and dependencies between chunks which at best introduces non-determinism and at worst pings off to infinity and crashes your game or at least makes it chug really badly instead you should always generate a fresh chunk this can be contained entirely within the same thread no problem at all and it won't be affected by anything happening in the Life game and to prevent any cyclic dependencies you can employ a simple rule you can only see what previous stages look like if you're generating trees it's okay to see what the top soiling stage did but if you tried
to see what the tree stage did then that'd be an infinite Loop and it would end badly this does mean if you want to be perfectly deterministic then you have to take snapshots of the chunk after each generation stage so you don't inadvertently leak details about what later stages did it has to be totally church and state you must never look into the future with that observation I built a system that implements all of that each generation stage can specify how many blocks around the chunk it needs to read and with that information I know
for example that grass generation will need the results of the previous generation stage for both this chunk and the chunk above it when the game asks for a chunk I can figure out which locations need to be generated and What stages they need to be generated up to if the necessary snapshots exist then the cached snapshot can be returned but otherwise anything that's missing is scheduled to be generated with the earlier stages going first the later stages only generate when they have all the snapshots they need from earlier stages as they declared earlier by specifying
the number of blocks around the chunk that they need the awesome thing is that those intermediate snapshots don't have to be shared between threads or even saved long term they can just be generated on demand as many times as needed and disposed of whenever we need to free up the memory the caching is an optimization rather than the fundamental necessity so there's just one remaining problem what about something like a tree which might start inside the chunk but Bloom out into adjacent chunks it's true that each generation stage can still only write to the blocks
inside its own chunk but this turns out to not be a problem in practice due to the con cep of scatter as Gather in short our tree generator is a scatter operation one thread writing out to multiple locations another example might be a gum blur where individual pixels scatter information out to nearby pixels it's the most natural way to write many operations but it requires sharing mutable memory which tends to involve expensive locks most parallel architectures whether CPU threads or GPU shaders don't play well of scatter operations instead they perform best by doing gather operations
where one thread is reading from multiple locations as an example instead of each pixel scattering its color to nearby pixels each pixel could independently look at all the nearby colors and combine them on its own while this inevitably duplicates some computation it doesn't matter because being totally independent means all the computation could be done all at once without mble shared memory which is why everything you do in a fragment Shader is almost certainly a gather operation rather than a scatter operation so how does this apply to us well our tree generator is a scatter operation
information about the tree starts from this chunk and emanates outwards into other chunks what we need to do is reframe it as a gather operation by having each chunk independently pull in the blocks that make up the tree that's what my abstraction does it reads in blocks from nearby chunks and discovers all the spawn points for trees who could intersect with this chunk it then runs the process to generate all of those trees and if any blocks land in the chunk they're written into the buffer because the process is fully deterministic the tree will generate
the same way each time ensuring it all lines up every single time as a result of this architecture there's practically no load time the world is ready to go almost immediately after starting and as I walk around the new chunks are generated in almost a single step I know it's overused these days but genuinely this is blazing fast and I couldn't be happier with it there is something I could be happier with though the rendering is looking a little bit tired which makes sense because it's mostly still the MVP renderer so it's pretty minimal also
the physics is quite buggy and you just get stuck sometimes which really sucks when you're exploring because it soft locks you and forces you to close the game I'd like to solve some of those problems in the future but in the meantime I have something to show you actually three things I've spent many years studying the music of Minecraft and breaking down how it works what I've realized is that the best tracks don't literally describe what's on the screen but instead evoke the emotions you're meant to feel that's the reason Minecraft is ambient rather than
chip tune but beyond that and this is seldom talked about the instrumentation is just as important Minecraft isn't just piano music though it certainly has piano it's much more about FM synthesis which is a kind of digital instrument all those Lush pads and soft sounds you hear all come from an FM synth and it's the way those syns mix into the natural instruments that makes Minecraft music so ethereal I could go on for much longer talking about attention management atinati and other composition and production things but I figured that get to in the weeds for
this channel so instead I decided to produce some demo tracks and you can listen to them right now they're designed to play with stin and acoustic instruments in much the same way that Minecraft does but with my own compositions My Hope Is that I can capture the same Vibe but with new compositions and high quality instruments I'll talk more about music and sound design at a later date probably when I actually implement it but for now head over to the new Daydream channel to hear all the tracks so far they're still drafts but I hope
you enjoy them nonetheless the links should be on screen right now I'll see you there