In my
previous post I mentioned that the atmosphere was "good enough" to move on to other tasks. But... the transition from day to night really bugged me... Simply put, I thought the transition looked too washed out, and I wanted to fix that.
My solution involved restarting from scratch and redoing the shaders, paying closer attention to the scattering process. The finalized effect proved to be much better than my first attempt, although with the downside of being limited to the 2.5% atmosphere height again. The visual thickness of 2.5% using the new shader was actually thicker than my first attempt set to 5% though, so I just left it like that. The visual benefit was well worth it:
The next step was fixing the clouds... The cylindrical texture I used before was not only low quality, but it looked like crap around the poles. I needed something that not only looked good, but was capable of zooming in all the way down to the ground level without looking seriously blurred out. Of course this isn't something that's possible using higher resolution textures. Some basic math: the circumference of Earth is 40,075 km. Max texture size in Unity is 8192. This gives the equatorial resolution of 4.89 km per pixel. That's pretty terrible. Another approach was needed.
After some thought, I decided to turn the cylindrical cloud map texture into a tileable square with the base size of 4096x4096. I used the basic projection logic (same one I used for the quad sphere) to calculate the UV coordinates both horizontally, and at the poles. This gave me better per-km resolution than using a 8192 texture with the added benefit of looking great both at the equator and at the poles:
If only it would still look great when zoomed in, right? Well... the most obvious way of adding detail at zoomed levels is to use detail textures. But... we're dealing with clouds, so what would the detail texture be? Why... the same exact texture! Instead of adding detail to an existing blurred texture, I decided to blend between the same exact texture, but sampled at two different resolutions (1x zoom and 4x zoom). The result was just denser clouds that could be zoomed in farther, but eventually still looked washed out when zoomed in enough.
That's when I thought to myself: why not do this continuously? Use the camera's height to determine the 2 closest zoom levels and feed these values to the shader. The shader will then use the 2 UVs to sample the cloud texture, then blend between them using the height-based blending value. I can basically continuously blend between 2 textures in an ever-increasing zoom level based on the camera's height:
if (heightFactor < 0.5f)
{
atmosphericBlending.x = 16f; // Tex0 UV coordinate multiplier
atmosphericBlending.y = 1f; // Tex 0 blending weight
atmosphericBlending.z = 8f; // Tex1 UV coordinate multiplier
atmosphericBlending.w = 0f; // Tex1 blending weight
}
else if (heightFactor < 2f)
{
float f = (heightFactor - 0.5f) / 1.5f;
atmosphericBlending.x = 16f;
atmosphericBlending.y = 1f - f;
atmosphericBlending.z = 8f;
atmosphericBlending.w = f;
}
else if (heightFactor < 6f)
{
float f = (heightFactor - 2f) / 4f;
atmosphericBlending.x = 8f;
atmosphericBlending.y = 1f - f;
atmosphericBlending.z = 4f;
atmosphericBlending.w = f;
}
else if (heightFactor < 18f)
{
float f = (heightFactor - 6f) / 12f;
atmosphericBlending.x = 4f;
atmosphericBlending.y = 1f - f;
atmosphericBlending.z = 2f;
atmosphericBlending.w = f;
}
else if (heightFactor < 54f)
{
float f = (heightFactor - 18f) / 36f;
atmosphericBlending.x = 2f;
atmosphericBlending.y = 1f - f;
atmosphericBlending.z = 1f;
atmosphericBlending.w = f;
}
else
{
atmosphericBlending.x = 2f;
atmosphericBlending.y = 0f;
atmosphericBlending.z = 1f;
atmosphericBlending.w = 1f;
}
And just like that, I had clouds that could be zoomed in all the way to the ground that looked fantastic both far away and up close. The blending between them was very subtle when traveling at the speed of a real-world rocket -- so subtle as to not be noticeable. Even with the camera test I set up that was moving very quickly the transitions were all smooth:
I made the clouds be affected by the scattering color for a more natural looking transition:
Next up came the night lights. The square nature of pixels was very noticeable up close. Something had to be done. The approach I settled on simply took the night lights and blurred them slightly, resulting in smoother edges. I then used a detail texture blended with the night lights map to create a much higher-than-original night lights map:
It still looks great even from far away:
The last thing I did was duplicate the cloud detail code in the shader and made it sample a noise map texture that I then applied to the terrain itself. This added a subtle variation to the terrain's texture that improved how it looks when zoomed in. The snow-covered mountain peaks still looked rather bad when zoomed in because there was a lot of contrast between the color of the pixels, but I addressed that by simply adding extra snow to make the transition less jarring. I did that by sampling the height map, disturbing it a little by using my trusty LOD-based noise map explained above, and adding extra whiteness around the cliffs:
Unfortunately there was nothing it could do to make the shoreline less blurry when zoomed in... but I have some ideas on how I can address that that I will explore further in a future post. I'm sure simply adding terrain deformations based on a heightmap + noise will improve the look quite a bit. But in the meantime I have some new decent looking 3440x1440 backgrounds!