While learning how to develop game engines in C#, I decided I wanted to
add some visual flare to my 3D world. Up until this point, I had been
leaving the sky of my 3D world bare, with only a flat color. I had the goal in mind to have a skybox with pleasing gradients,
moons, stars and a sun which rises and sets.
Here is what it ended up looking like:
The sky itself was rendered on a small inverted cube at the center of the world, with its matrices cleared so only projection and the views rotation were taken into account. This makes it so it appears that the camera is always at the center of this small cube. In the fragment shader of the skybox's inner walls, a position on a sphere can be calculated for each fragment by normalizing it. This gave me a unit sphere of fragments to work with, which allowed me to remove any resemblance of a 'box' such as corners. The z-depth of the skybox was rendered to be behind everything else in the scene, which made it appear very far away in all directions.
Once I had this inverted sky-sphere, I was able to change the color of
fragments based on their height. I then added a uniform for a sun
direction vector, which I could use to calculate each fragments distance
to the sun in the sky, as well as the height of the sun.
Using these variables I could change fragments color and brightness based
on the height of the sun, the distance of the fragment above the horizon,
and the distance to the sun in the sky.
After adding uniform variables for the different colors in the sky (e.g. sun
set color, sun rise color) I moved on to rendering an actual sun. I achieved
this by rendering a GL_POINT which orbited around the center of the skybox.
It had a shader which made it circular with a transparency fade to give it
the illusion of brightness and atmosphere.
To give the illusion of a horizon, A second box needed to be rendered to
cover up the bottom half of the skybox with black. The depth of this box was
still very far away, but made to be closer than the sun or sky, which
covered them when rendered. When the sun gets closer to the horizon, that
area of the sky can be made to change color in the fragment shader based on
the sun's position using dot products. This gives the illusion that the sun
is going over the horizon, and lighting up the atmosphere.
Now that I had a decent daytime sky in place, I now needed to add some
moons and stars for the night. I did this by having each moon as a GL_POINT
sprite displaying a moon texture on a circle, and the stars being a very
numerous group of very small GL_POINTS which was originated at the center of
the skybox. This group of points had their positions normalized so that they
formed a sphere of points. Each star and moon was generated with a random
size, brightness and color based on a seed. The stars rotated at the same
rate and same direction as the sun, and the moons each had their own special
rotations and timings.
The moons' transparency could be changed based on their proximity to the
sun, and the time of day, giving the illusion that they are in the sky
during the day but overpowered by the suns brightness. Due to how
transparency works, if a moon was be close enough to the sun to cover it,
the moon would be visible as a dark circle, similar to a solar eclipse in
real life.
Although it was quite tedious to adjust the shader over and over to test
different variables, I quite liked the results. The performance was also
very acceptable, with only 5 draw calls needed for everything total. There's
also no ray casting calculations being done to approximate atmospheric
scattering, so its quite efficient. It is also all done with no post
processing effects (except for bloom in some screenshots). This makes it a
great system to work on and use in the future, in combination with clouds,
time-based world lighting and some nice volumetric fog 😊.