Building a Flexible Day/Night Cycle with Simple Parts
The sky is an important character in Eastshade. In addition to taking up a large portion of the screen at any given moment, it also gives players the sense that the game world has its own beating heart. While I knew I wanted to make the sky spectacular and dynamic, I also didn't want to spend tons of performance or development time on it. While something like dynamic volumetric clouds can look absolutely awesome and convincing, they are out of my comfort zone technically and are very expensive to render at high enough detail to look realistic. After all, Eastshade is not an airplane simulator! So I opted for a solution with simple parts that I could hone easily with my artist's eye, rather than a more procedural approach.
The Day Cycle Manager
Firstly, I knew there were going to be a bunch of values to tweak for each distinct time of day, so I wanted one central place to save/load and tweak them all. Since the sky setup is made of quite a few parts and shaders, I was led to create what I call the "DayCycler" component, which looks like this:
This custom inspector may look formidable with its many fields and values, but it's really just a bunch of references to material values, light values, and rotations pertinent to the global lighting. Inside this component is a simple update loop which interpolates between the most recent and incoming time of day presets. With this simple system, I can go through and tweak each distinct time of day like a painter, and all the in-between times are taken care of for me. No need to manage 20 different IPO curves. The times are 0-1 rather than 24:00 so don't be confused by times like 0.175 (that would be something like 4:12 AM). There are a lot of things in the world that look at the time of day which are more local, such as a a hanging lantern, or chirping birds. Little lights and audio sources like these drift in and out of memory as the world streams around the player. Referencing and managing all these little things in this central controller would be quite cumbersome, so the second part of this system is this little component:
I attach this to any little light or audio source that is day/night dependent and it looks at the current time of day and decides what value it should be independent of the DayCycler. No need to micro manage values like these.
The "sky" is composed of a few different elements:
Fog - Starting with the closest and moving out, we have the all-important fog. I use the fantastic Fog Volume for this. The reason I love fog volume is because of its gorgeous and fairly cheap light in-scattering. If you've never heard of in-scattering, I suppose it can be described as the look of sunlight passing through fog. I'm not talking about god rays (though in real life I believe its caused by the same thing). It adds a lot of depth and a sense of light direction to the atmosphere.
Clouds - Call me old fashioned, but I like the look of photo clouds. The biggest issue with photo clouds is that it is difficult to make them dynamic. My strategy was simply to take photos on a day that the clouds didn't have a strong sense of light direction, combined with touching up the parts that look too directional. Once I had my 360 degree cloud panorama, I made an alpha mask cutting out the blue parts because I wanted to encapsulate the clouds seperate from the atmosphere. I mapped my clouds to a dome that rotates slowly to give the impression that the clouds are moving along the horizon. This trick is stupid simple, and is ineffective for giving players the impression that clouds are passing over them, so if you want that you will have to combine this with other methods like overhead UV scrolling clouds or something like that. I actually haven't gotten around to doing overhead clouds yet, but funnily enough players tend to rarely look directly up and haven't noticed.
Atmosphere - I find a simple 2-value gradient shader on a dome mesh is sufficient for the atmosphere. The opacity and colors animate with the day cycle. I increase opacity near the horizon so it looks thicker, while the stars show through more when you look directly up.
Sun - There are two parts to my sun. I have a sun flare, and an actual sphere mesh with a highly emissive solid color. This way I get a nice bloom, even if the sun is only partially showing. This is particularly useful for me in Eastshade, as there are daily eclipses and I needed a way of showing the sun slowly hide or emerge from behind the moon. The sun's directional light doesn't actually move around in the sky, it just rotates. To keep the sphere lined up with the flare, I rotate the sphere around the player's head, rather than around its own center.
Moon - I designed a custom shader for the moon. It's anything but physically accurate. It expands the light angle a bit, and uses heavy fresnel to fake the bending of light around the atmosphere. I have a special light that shines on the moon alone to simulate the sun hitting it. Here's a bit of Eastshade trivia regarding its moon:
The moon in Eastshade appears habitable, and since its about the same size as the planet you're standing on, you orbit it as much as it orbits you! In other words, both planets are moons to one another. This means Eastshade's moon remains in the same place in the sky all the time, which creates daily solar and lunar eclipses. At midday it blocks you from the sun, and at midnight you block it from the sun. Tidally locked, you orbit around each other in a double planet dance all the way around the sun. Is there another world of intelligent life just across the cosmic pond? The residents of Eastshade can't know. All they can do is look up and wonder...
Space - The furthest background is the space dome. This dome has a tiling star texture, supplemented with bits of geometry for the larger stars to break up the tiling. The reason I use geometry to break up tiling is because having a texture that wraps around the whole sky would require a MASSIVE texture to look sharp. The fact that stars are tiny little dots means they live or die on their sharpness. If I wanted to add a nebula or something like that I'd probably have that as a decal sticker on the star dome, because doesn't tile and doesn't need as much resolution as the stars. I'm trying to keep memory and build size down, mostly because I don't want to waste development time maintaining a huge build.
Finally, its important to note that all these things follow the player around as they move. Since most of these things are supposed to look infinitely far, there shouldn't be any parallax between the elements.
I'm not done with the sky systems in Eastshade. There are a few things left to do. Among them is come up with some sort of overhead cloud coverage to pass over the player. I'm thinking I will put a flat disc and use vertex color to taper the opacity around the edges. Then I'll scroll the UVs over a tiling cloud texture. I also want to have multiple cloud textures for different weather conditions and fade between them. I'll need to make a weather controller that operates on top of the DayCycler and plays off the base values, so I can have any weather condition at any time of day. I'll need to implement a global wetness property in my shaders that increases gloss and spec, while darkening the diffuse a bit.