Hybrid approach for procedural planets
Part VII - Improvements and bug fixes
I am writing the code for this tutorial at the same time as writing the tutorial, naturally a bug or two will creep in.
Thankfully I have a team of proof readers who very kindly go through my code with a fine toothed comb.
So we had best fix the first problem they found. The terrain generation uses the UV coords of the sphere to plot the results. Thus at the poles of the planet we end up with crenalations. I forgot to take care of this before the terrain is passed to the normal mapper.
This resulted in seems being visible at the poles, hardly what we want. Thankfully it is easy to fix.
In Planet.cs , at the spot in the generate method just before we start calculating the normals, insert the following code.
Thankfully I have a team of proof readers who very kindly go through my code with a fine toothed comb.
So we had best fix the first problem they found. The terrain generation uses the UV coords of the sphere to plot the results. Thus at the poles of the planet we end up with crenalations. I forgot to take care of this before the terrain is passed to the normal mapper.
This resulted in seems being visible at the poles, hardly what we want. Thankfully it is easy to fix.
In Planet.cs , at the spot in the generate method just before we start calculating the normals, insert the following code.
#region Fix the crenalations at the top and bottom of the heightmap // Fix the heightmap at the poles. int max_x = (TextureHeight - 1) * TextureWidth; for (int x = 0; x < TextureWidth; x++) { Map[x + max_x - TextureWidth] = Map[x + max_x - (TextureWidth * 2)]; Map[x + max_x] = Map[x + max_x - TextureWidth]; Map[x + TextureWidth] = Map[x + (TextureWidth * 2)]; Map[x] = Map[x + TextureWidth]; } // max_x -= TextureWidth; for (int x=0; x < TextureWidth; x+=16) { for (int y = 0; y < 32; y++) { int i = y / 2; for (int j = i+1; j <= 16; j++) { Map[x + j + (y * TextureWidth)] = Map[x + i + (y * TextureWidth)]; int mx = x + j + max_x - (y * TextureWidth); if (mx<TextureWidth*TextureHeight) Map[mx] = Map[x + i + max_x - (y * TextureWidth)]; } } } Mercator.SetData<Color>(Map); #endregion |
I have also started using two variables TextureWidth and TextureHeight instead of hard coded values, just so you can easily change the size of the texture maps.
Okay now for some improvements. Having only five planet types is not very realistic. In our solar system we have four gas giants, and they break down into two types. Traditional (or Jovian) gas giants are mainly comprised of Hydrogen and Helium. Jupiter and Saturn are of this type. Ice giants are comprised mostly of Water, Ammonia and Methane. Uranus and Neptune are of this type. These two types look very different, and that's all we know about so far. What else is out there?. So we need to add variation.
Luckily I planned ahead for this. Remember I used a 2D texture for colourising the planets instead of a 1D textre? Well this is where it comes in handy.
Split the texture into vertical bands and put a different colour map in each one. I have decided to have eight sub types for this demo. So grab a copy of this and replace gas2.png with it.
Okay now for some improvements. Having only five planet types is not very realistic. In our solar system we have four gas giants, and they break down into two types. Traditional (or Jovian) gas giants are mainly comprised of Hydrogen and Helium. Jupiter and Saturn are of this type. Ice giants are comprised mostly of Water, Ammonia and Methane. Uranus and Neptune are of this type. These two types look very different, and that's all we know about so far. What else is out there?. So we need to add variation.
Luckily I planned ahead for this. Remember I used a 2D texture for colourising the planets instead of a 1D textre? Well this is where it comes in handy.
Split the texture into vertical bands and put a different colour map in each one. I have decided to have eight sub types for this demo. So grab a copy of this and replace gas2.png with it.
Now we need to tell the shader to use it. In all the shaders add a variable float subtype at the top of the file. Then add change the following line of code in ColorMap.fx and Molten.fx
// float4 texCol = tex2D(PallSampler,float2(0,height.x)); float4 texCol = tex2D(PallSampler,float2(subtype,height.x)); |
Now we need to setup subtype. Add a public variable int SubType to the top of Planet.cs and add this line of code to the draw routine.
draw.Parameters["subtype"].SetValue(SubType / 8.0f); |
Okay the last thing we have to do is setup SubType. In the BuildPerm method of Planet.cs we just need to add one line of code right at the end of the method.
SubType = r.Next(8); |
We now have support for eight subtypes in all planets, but so far all we are doing is changing the colour. We really need to add more variation in the generated textures as well, so lets start with the GasGiant.fx
Again simplex noise shows it's flexability. In the pixel shader we calculate dr based on a 3D scaled vector. Simply by changing this we generate a lot of variation in the resultant texture. Change this one line of code.
Again simplex noise shows it's flexability. In the pixel shader we calculate dr based on a 3D scaled vector. Simply by changing this we generate a lot of variation in the resultant texture. Change this one line of code.
// float3 dr = float3(2,res+10,2); float3 dr = float3(subtype,res+10,subtype); |
I suppose I better show you some results....
Now onto Ice planets. The original colour map assumed water ice, but the temperature of the planet could be low enough for what we think of as gases to become ice. Oxygen freezes at about 50 Kelvin. Also Ice planets may have an atmosphere which absorbs light of various colours.So we can do the same trick with the texture as we did with the gas giant. If you have the time you could research absorption spectra and come up with something realistic, but I will just throw in a few bands.
Again let's change one line in the pixel shader to produce more variation in the results.
// y=(y+1)/2; y=(y+1)/(1+(subtype*4)); |
Again you should play with the code and the texture to get what you want, this is a guide not a bible.
On to Water planets. I called them water planets, but of course what they really represent is planets with a hydrosphere of some type. We expect water, but it could be other chemicals. The atmosphere can absorb light. etc. etc. etc. I'll just use the same trick.
On to Water planets. I called them water planets, but of course what they really represent is planets with a hydrosphere of some type. We expect water, but it could be other chemicals. The atmosphere can absorb light. etc. etc. etc. I'll just use the same trick.
Again we will add more variation to the height map by changing one line of code
// freq = (2*pow(2,i))-1; freq = (2*pow((2+subtype),i))-1; |
I hope this has given you a few ideas of how to add variation to you planets. Play with the textures and shaders to get something you really like.
Share and enjoy, it's here.
Okay now we will move onto the really hard code, atmospheres.
Next part of the tutorial
Share and enjoy, it's here.
Okay now we will move onto the really hard code, atmospheres.
Next part of the tutorial