Solar system with procedural planets
Table of contents
- Plan and thought process
- Different PCG techniques
- Vornoi diagram
- Perlin noise
- More shapes and effects
- The solar system
- Further improvements and opinions
1. Plan and thought process
1.1 Why this idea?
My idea for the R&D assignment is to create procedural planets in a solar system. My main question is “How can I create procedural planets in a solar system”. I decided to choose this topic for my R&D for three reasons, 1) I’m interested in physics and astronomy, because it interests me a lot why we exist and what’s out there, 2) I hope to learn a lot more about how to program shaders, because creating something pretty visually is really cool, and 3) I want to learn and read more about the different PCG techniques and how to apply a some of them.
For inspiration I looked at some generated planets in games, like No Man’s Skye and (thanks to the guild meeting) to Spore. I also looked on Pinterest (Procedural Planet Examples, n.d.) (Wikipedia contributors, 2020).
1.3 The scope
During the first week of development, I had some issues with my scope, because I wanted to do too much in a short period of time (I also got feedback during the guild meeting that my scope wasn’t clear). I kept coming with ideas while not knowing if I had the time and knowledge to realize them. But after talking with one of my teachers I got a clearer vision:
The planets are not walkable, but to be looked at from a far. The planets will be of a certain type depending on their position to the sun. In our solar system there are three types of planets: Rocky, Gas and Ice planets (Scagell, 2008; Wikipedia-bijdragers, 2020). Also in my solar system I will determine the planet type based on the planet’s distance to the sun. Below I will define the possible properties of each of these planet types.
- Will be closest to the sun.
- Stone surface / mountains.
- Metal and stone.
- Brown colors, with white stripes.
- Seem to be much bigger compared to the other planets.
- Middle orbit radius.
- Low temperature.
- Much blue and white colors.
- Furthest from the sun.
Later in my development process I added another type of planet, which is called a Fire planet (one planet closest to the sun). This type of planet doesn’t actually exist in real life, however I wanted to make the solar system a little bit more interesting.
My plan was to make the following variables. I made the things I was able to complete during the R&D period bold.
- Newton’s laws / gravitation law to make the planets orbit the sun.
- Gas (stripes)
- Skybox star shader (optional).
- Moons and rocks (optional).
2. Different PCG techniques
In this chapter I will give a brief description of a few PCG techniques and in the next chapter I will show and explain the techniques I implemented, in more detail. I couldn’t really start the project without having some knowledge of what’s out there.
2.1 Voronoi diagram
- Collection of random points placed on a surface. Based on the distance of each pixel and a point cells are created.
- Can be used to generate all sorts of patterns like water and maps (Voronoi Partition | Patterns | Computer Animation | Khan Academy, 2019, 03:15–05:21).
2.2 Perlin noise
- Creates points within a range of 0 (white) and 1 (black) based on the values of the other points (so not randomly).
- For example height and color can be dependend on these point values.
- Can be used to generate various effects with natural qualities like, clouds, landscapes and patterend textures (Perlin Noise (Article) | Noise, n.d.).
2.3 Cellular automata
- Collection of cube like cells. Each cell lives on a grid (environment), has a certain state and a neighborhood (surrounding cells).
- “Now consider a set of simple rules that would allow that pattern to create copies of itself on that grid. This is essentially the process of a CA that exhibits behavior similar to biological reproduction and evolution.”
- Create a new cell value for the new generation, based on the neighbor’s state. What comes on the question mark needs to be decided by a rule that the developer defines. For example 101 will be 0 and 001 will be 1 etc.
- Each rule creates different shapes. Cellular automata can be used to create procedural terrain or shapes (Shiffman, 2012).
- L-systems have rules which look a bit like cellular automata, but have very different rules. Its rules are in string form based on a character its neighbors.
- There a lot of types of different L systems (MORPHOCODE, 2018).
- Splitting a grid into cells (until it can’t be split anymore), rectangles in these cells and connecting them.
- Good to use for dungeon generation (Basic BSP Dungeon Generation – RogueBasin, n.d.).
- Programmed set of rules.
- Good to use for dungeon generation or maps (Procedural Buildings, Part 3: Using Grammars, 2019, 03:15–05:21).
3. Voronoi diagram
3.1 How it works and implementation
I decided to create a Voronoi diagram, because this could help me to create water and other interesting shapes. The book of shaders by Gonzalez Vivo & Lowe (2015) helped me understand how to Voronoi works. The first step to create a Voronoi diagram is to divide the particularly surface into tiles (squares; see the red lines). This will be done based on the incoming UV coordinate.
In which tile an UV coordinate exists, depends on its value. The function floor creates rounded numbers based on the UV coordinate. For example if the UV coordinate is (0,5;2,4), floor will convert this to (0,2). So this UV coordinate’s tile is (0,2).
In the decimal variable is a function called frac used, which only takes the decimals of a value. So the UV coordinate I had mentioned would with the frac function turn into (0,5;0,4). I will later on explain why I need these decimal numbers (Gonzalez Vivo & Lowe, 2015).
In the next piece of code I loop through each tile that is a neighbor of the tile where the current UV coordinate is in. Each loop starts with -1 and ends with 1, because these values are the tile positions of the neighbors (see picture for clarity) (Gonzalez Vivo & Lowe, 2015).
Then I define a random value in a particularly tile, because each tile needs a point. The following makes the point move fluently. Each run its subtracting this decimal value, which creates the movement around the tile (if multiplied with the time).
But always in the exact same way, because this decimal number never changes (an UV coordinate is constant) and neither does the random vector for each UV coordinate. I used the unity random vector function to choose the random coordinate. I didn’t want to spend too long creating a random function (this is not build in, in shaders), because I wanted to focus on things that had a higher priority.
Furthermore, I keep track of the minimum distance. This means I check which point the UV coordinate is closest to and return this (Gonzalez Vivo & Lowe, 2015).
This is all done in a separate cginc file, because I like to keep methods that need to be repeated in different shaders in a separate file. So it’s accessible from any shader file. For this shader I used a standard surface shader, mainly because shaders were and still are quite a new thing to me. When setting the color, with _Cellsize I don’t mean the actual cellsize, but how much of the cell is using the _RippleColor (Brackeys, 2019, 03:15–05:21).
I added this simple line in the surf method and this was the result. I did change the cellDensity and the angleOffset for both the sphere and the quad.
The cellDensity variable is used to increase the tile size. So the bigger the cellDensity the bigger the cells/tiles. The angleOffset is used to make the random function more random. The random function is not actually that random, because you keep feeding it the same UV coordinates so you’ll receive the same result each time, but with a different offset it looks like it is random. The offset is comparable with a seed. This is useful when having multiple Voronoi planets, because I don’t want the cell positions to be exactly the same for each planet (Brackeys, 2019, 03:15–05:21).
With actually not that much code, I can already create a lot by tweaking the variables and unity’s tile offset.
Creating a star like skybox was quite easy once I had the Voronoi diagram. It just took me some time to realize that a standard surface shader is obviously not going to work for a skybox, because it’s not a surface. So I used an unlit shader (Boysen, 2020).
3.3 The sun
For the sun I used the already explained Voronoi shader and tweaked the values a bit. This was clearly not enough, because my sun looked like an orange. So I created particle system (after someone advised me to) in the form of a sphere (placed it behind it) and added some lights in the scene (should actually be mentioned in the solar system chapter). The left picture is the ‘old’ sun and the right picture is the improved one.
4. Perlin noise
4.1 Perlin noise mesh
Perlin noise looked like a good PCG technique to create mountains and other interesting patterns. Especially mountains, because Perlin noise creates a pattern which is not completely random. Each point depends its variable on its neighbor, which creates shapes. Bumping up the height of these shapes, creates mountains.
There are a few dimensions of Perlin noise. I’m using 2 dimensional Perlin noise (for everything), because I want to map the noise on the sphere.
The very first thing I actually started on in this project was creating mountains on a sphere. And this was the embarrassing outcome. This took me too long. I think the main problem was that my scope was all over the place and I was too stubborn to ask for help.
The feedback I got during the guild meeting, was to use a high res sphere and this fixed the problem of the ugly mountains. They were much more detailed now. I also got the feedback to use 2D noise instead of the 3D noise I was using before. I did spawn a sphere inside the noise though, because it would be much easier to apply a separate shader to it later on.
This is the working code. I applied the knowledge taught in the Mesh generation lesson. So I’m accessing the vertices and normals of the sphere. Changing a shader variable (green lines), based on the sphere’s size. Then I apply the noise for each vertex in the direction of its normal.
The higher the frequency the more zoomed out the noise is resulting in many mountains. The lower the frequency, the more zoomed in the noise is resulting in one big mountain (Red Blob Games, 2020).
Octaves are a sum of multiple noise with different or the same frequencies (Red Blob Games, 2020).
Decides the flatness of the mountains (Red Blob Games, 2020).
4.1.3 Mountain shader
I’m using a standard surface shader to draw the green line around the mountain. I’m using the function lerp between two colors with as T value; the distance between the objects origin and the world position of the pixels (and not the UV, because the UV is relevant to the object itself and not where it is placed in the world). _Radius is the height of the color.
4.2 Perlin noise shader
Just like in the chapter about Voronoi I’m first dividing the UV’s into tiles. Instead of looking at the neighbor tiles, now it is important to look just at the corners (float2’s) of the tile and pick a random value for each corner.
The Dot product and the lerp function makes the values come out more smooth/blend than harsh black and white values. I’m not 100% sure how the dot product helps with this (the math), and it’s also not exactly explained in the article I read.
5. More shapes and effects
5.1 Fresnel effect
I used a Fresnel effect for some planets, because it can these planets look a lot better. A Fresnel effect is basically a blended outline. It will get rid of hard shadows and adds a somewhat glowing effect (Böhringer, 2018).
First I use the dot product between the worldNormal and the viewDirection, because the effect should always face the camera. The dot product calculates the distance between these two vectors. If I would not use any color the Fresnel effect would look like the first image. The middle results to be black, because that’s the part where the distance between the viewDirection and the worldNormal is smallest. If this distance would for example be 0.1 minus one is 0.9, which is nearly black. If the distance would be 0.5 minus 1 is 0.5, would be greyish (Böhringer, 2018).
Saturate clamps value to be between 0 and 1 to prevent odd results. The entire thing will be minus one, otherwise the center will have the effect, but not the edges. So it’s basically inverting the effect. The bigger the _Power variable, the stronger the effect will be (Böhringer, 2018).
Finally you set the Fresnel effect and object its color, equal to the Emission . The emission controls the color and light intensity on the service. “When an emissive material is used in your scene, it appears to be a visible source of light itself” (Böhringer, 2018).
6. The Solar System
6.1 Setting up the solar system (solarsystem.cs)
In this paragraph I will explain how I set up the solar system. In the Start function made sure the planetCount are divisible by three, because there are three official types (I will later explain what I did with the Fire planet type). I instantiate all planets and set their type. I will later on explain how I made the planets orbit.
This function places all planets behind each other with currently a distance of ten.
The SetType function sets the planetType based on the positions in the array. Index zero for example will be the closest planet to the sun. Because I decided to add a Fire planet later on, I didn’t want to rewrite all code, so the first planet will always be Fire planet.
The different types are defined in an enum.
6.2 Randomizing between planet variables (Planet.cs)
I randomized between planet variables by creating a few materials, copying these materials through code and adjusting the shader variables. So I’m not directly changing an existing material.
I used a switch statement to give the right method to the right planet type. In each method (for this example the GasPlanet method) I copy a particular material. Otherwise I have to set a lot of shader properties, which will be too many lines of actually unnecessary code.
When copying a material I set its shader type (because it firstly needs to have the same shader to be copied). Then I copy the property values from the original material to a new material.
I put all the paths to the shaders in a separate class to prevent typos and for easy implementation.
6.3 Newton’s law of universal gravitation
To orbit the planets around the sun I used Newton’s law of universal gravitation. In 1687 Newton published the law of gravitation (Dalen & Dongen, 2013). This law explains the gravitational force between two objects. The bigger the mass, the bigger the gravitational force. So the value of the mass can influence how much two planets attract each other. This explains why all planets in our solar system orbit around the sun, because it has the biggest mass of all planets in our solar system. If the gravitational force is multiplied by two, means the orbit radius will be two times smaller, because the force/attraction is bigger (Dalen & Dongen, 2013).
- Fg is the gravitational force in Newton
- G is the gravitational constant in Nm^2/kg
- m is the mass in kg (planet 1)
- M is the mass in kg (planet 2)
- r is the distance between the objects their center
In the following image, I created a function to calculate the force between the sun and a particular planet. Based on the distance and mass between the planets I calculate the gravitational force and apply it in the sun its direction. My force wasn’t strong enough, so I multiplied it by the force strength, which currently is 5 (Dalen & Dongen, 2013, pp. 1–3).
Two things I did wrong while implementing this are; 1) I added a start velocity in the fixed update, 2) I didn’t use ForceMode.
During the guild meeting one of my teachers explained to me that the velocity isn’t added constantly, but only one time. I wonder how this worked in our solar system. Did all planets after the big bang just suddenly got a start velocity? So I set up a start velocity and added this force in the Start function.
Force mode gives the rigidbody more details on how the force should affect the game object. ForceMode.VelocityChange speaks for itself. Impulse is used to apply a force immediately based on the mass (source is unity’s ForceMode manual).
Now the planets are orbitting the sun! Although some actually go inside the sun. I didn’t have enough time left to really research this and change this. So this will be one of my future plans.
7. Further improvements and opinions
I think my project went ok. I just have a hard time admitting I did something right for some reason.
I learned that shaders are actually not that scary as I thought. I did pass the first shader assignment, but I can’t say that I had an amazing result immediately and that’s also how it went with this project. But each time you work on it you learn more and start to get comfortable with how HLSL looks. So if you are like me, thinking shaders are extremely hard to learn, stop thinking like it as an obstacle and just do it. In the first week I was quite stubborn and wanted to do it all alone. But I realized I needed to ask for help and did so the other weeks.
I’ve got quite a few future plans for this project, because this merely just the base. I also figured out how I could’ve done certain things in a different way next time.
- Creating my own sphere: I’ve used my spheres in a tricky way, I’ve spawned the unity in a high res sphere and shown them on different occasions. That’s not how it’s supposed to be or at least how I want it. Finding a good high res sphere was also quite hard. So making one is 1) pretty interesting, 2) much better, cause I can manipulate the sphere exactly how I want it to look (faces and vertices).
- Glowing sun effect: In the future I hope to make some kind of glowing effect shader, because I rather do this with a shader than a particle system, but I didn’t have enough time unfortunately.
- Reflective Ice.
- Fixing my clouds. I started working clouds. With a _backgroundtexture + perlin noise it looked good, because I could see the skybox throught the gaps. But once I placed it infront of another planet, I couldn’t see that planet anymore.
- Of course trying out the other PCG techniques available. There are way more than I mentioned and they will also cause some interesting patterns.
- I want the planets to not go inside the sun. For now I’ll just say it’s a feature.
- I really wanted to make some sort of menu where you can see all the spawned planets and follow them around.
Basic BSP Dungeon generation – RogueBasin. (n.d.). Roguebasin. http://www.roguebasin.com/index.php?title=Basic_BSP_Dungeon_generation
Böhringer, R. (2018, May 26). Fresnel. Ronja’s Shader Tutorials. https://www.ronja-tutorials.com/2018/05/26/fresnel.html
Boysen, J. (2020, June 5). Creating a procedural skybox in Unity’s Shader Graph. Medium. https://medium.com/@jannik_boysen/procedural-skybox-shader-137f6b0cb77c
Brackeys (2019, May 19). SIMPLE CARTOON WATER in Unity [Video]. YouTube. https://www.youtube.com/watch?v=Vg0L9aCRWPE
Dalen, B., & Dongen, J. (2013). Systematische natuurkunde Vwo 4 Basisboek (8th ed.). ThiemeMeulenhoff bv.
Gonzalez Vivo, P., & Lowe, J. (2015). The Book Of Shaders. The Book of Shaders. https://thebookofshaders.com/12/
MORPHOCODE, M. (2018, May 9). Intro to L-systems. MORPHOCODE. https://morphocode.com/intro-to-l-systems/
Perlin noise (article) | Noise. (n.d.). Khan Academy. https://www.khanacademy.org/computing/computer-programming/programming-natural-simulations/programming-noise/a/perlin-noise#:%7E:text=Perlin%20noise%20has%20a%20more,the%20smoothness%20of%20the%20curve.&text=Contrastingly%2C%20the%20next%20graph%20below%20shows%20pure%20random%20numbers%20over%20time.
Procedural Buildings, Part 3: Using Grammars. (2019, July 5). [Video]. YouTube. https://www.youtube.com/watch?v=zCeqBV0Amm0
Procedural planet examples (n.d.). Pinterest. https://nl.pinterest.com/pin/8936899245394568/
Red Blob Games (2020). Making maps with noise functions. Making Maps with Noise Functions. https://www.redblobgames.com/maps/terrain-from-noise/
Scagell, R. (2008). De Ruimte Verklaard. Bergmans.
Shiffman, D. (2012). The Nature of Code. The Nature of Code. https://natureofcode.com/book/chapter-7-cellular-automata/
Voronoi Partition | Patterns | Computer animation | Khan Academy. (2019, April 24). [Video]. YouTube. https://www.youtube.com/watch?v=Q804hv73L6U
Wikipedia contributors (2020, May 13). List of games using procedural generation. Wikipedia. https://en.wikipedia.org/wiki/List_of_games_using_procedural_generation
Wikipedia-bijdragers (2020, July 27). Aardse planeet. Wikipedia. https://nl.wikipedia.org/wiki/Aardse_planeet