Pages

26 October, 2021

My Dynamic Skybox in C# OpenGL

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 😊.