Dynamic Day-Night Cycle in 3D Scene
Obed Kobina Nsiah, Fedir Kyrychenko
Live Demo Site Github Repository
Project Description
This project delivers an immersive experience of a 3D day-night cycle. Using the Three.js library, the scene simulates real-world twenty-four-hour time transitions by adjusting the environment's light, colors, and shadows based on the user’s local time. It's inspired by the Dynamic Wallpaper on MacOS, which shows a visually engaging change in atmospheric and light states in the morning, afternoon, evening, and night.
We have chosen to emulate the charm and simplicity of the Minecraft aesthetic and use voxel models for a playful, blocky, low-poly look. This project is a voxel engine created using the Three.js library. The main idea is to render procedurally generated worlds with camera movement in Fly Controls style and realistic global illumination effects using SAO (Screen-space Ambient Occlusion) and FXAA (Fast Approximate Anti-Aliasing). The placement of objects, such as light sources and clouds, is dynamically configured, and graphics and performance settings are loaded from an external JSON file. Other voxel models in the scene are created using Magicavoxel.
How it was before:
How it is now:
Final Result - Implementation Description:
Chunks generation:
We use a chunk-based system here: each chunk is 16×16 blocks in area and 32 blocks high. Perlin noise is used to determine the height,and depending on that height, a specific block is placed. If the height is below the required minimum, the empty cavities are filled with water, forming lakes.
Dynamic Foliage Coverage:
On top of the generated structure of soil, water, sand, and stone, each top block in a chunk dynamically receives foliage based on its type. Soil blocks have a chance to spawn trees, grass, flowers, or bushes. Sand blocks may grow reeds, while surface water blocks can freeze into ice or be covered with lily pads.
Visible-Only Block Rendering:
Only blocks without neighboring blocks are rendered. The rendering script is optimized to check whether a block touches blocks in other chunks, ensuring that only the visible sides of blocks are rendered for the user.
Instance Mesh Rendering:
Since the project involves a large number of identical textures, it was decided to use instance mesh rendering to reduce the time required to send each texture for rendering. This significantly improved the rendering speed.
Looped 16x16 Water Animation:
The water animation is created using a looping 16x16 pixel video texture.
Bump effect & Mipmap Optimization:
Techniques such as bump mapping and mipmapping were employed to improve the overall image quality. These enhancements slightly improved visual perception and resolved certain rendering issues.
Ambient Light and Nocturnal Glow:
Daytime lighting is achieved using ambient and directional light, while the glow of nocturnal plants and their blooming effects are created with emissive materials and point light sources.
Properties of the 6 Segments of the Day-Night Cycle
When a user opens the live scene, they see one of the following six-day segments (dawn, morning, afternoon, dusk, evening, midnight) depending on their current local time. To capture the essence of each segment, we manipulated the following properties:
- Fog Color and Density
- Ambient Lighting and Intensity
- Directional Lighting Color and Intensity
- Sky Mesh Top and Bottom Colors
Initially, we were using the background property of the scene to change the coloring of the sky, but that limited the sky to only one solid color, which was not ideal since, in the real world, the sky is usually a gradient of colors. To bypass this, we use the sphere geometry in three.js to create a large sky dome and add a custom shader material. The shader creates a gradient effect by blending two colors: a top color for the sky and a bottom one. The camera is placed within this sky mesh so the inner faces of the mesh are rendered. The coordinates of the sky mesh are always updated anytime the camera is moved to ensure that the camera remains centered inside the sphere.
Description of each segment
Dawn: This starts at hour 5, ends at 6, and transitions smoothly to the next segment until 7. We use a high value for the density of the fog to mimic morning mist. The ambient light is a warm color with low intensity to match the light blue - pink color gradient of the sky. The directional light is an orange color with a little intensity to mimic the rising of the sun.
Morning: This starts at hour 7, ends at 11, and transitions smoothly to the next segment until 12. For this segment, we reduce the fog slightly for better visibility and use a soft yellow light for the ambient and directional lights for a sunlight effect. The upper gradient of the sky is blue, but use a yellow color for the bottom to mimic a light yellow horizon.
Afternoon: Because this is the brightest of the segments, we use a very low fog value for clear visibility. The ambient light is bright white with high intensity and the directional light is bright yellow with a max intensity. The upper color of the sky is a bright blue, and the lower color is yellow. This starts at hour 12, ends at 16 and transitions smoothly to the next segment until 17
Dusk: Since this is the sunset segment, we use a golden yellow for the ambient light and a deep orange for the directional light with both of these having their intensity decreased. The fog density is increased slightly and the sky goes from a pinkish color blending into an orange one. This segment starts at hour 17, ends at 18 and transitions smoothly to the next segment until 19
Evening: This starts at hour 19, ends at 21, and transitions smoothly to the next segment until 22. The fog is increased slightly from the previous one to decrease visibility. We use a dark blue color for both the ambient and directional lights with low intensity. The sky goes from a dark blue into dark brown.
Night: Since this is the darkest hour of the segments, The fog density is high. We use a gray for the ambient and directional lights with low intensity. This segment starts at hour 22, ends at hour 4, and transitions smoothly to the next segment until 5. The sky gradient goes from dark blue into black.
Transitioning Between Segments
An hour before the start of the next segment in the day, we use a linear interpolation function to smoothly blend each of the properties described for the current and next segments. For every minute in this one hour window, the lerp function gradually changes the aforementioned properties until the properties of the next segment are in full effect at the start of its hour. This helped to avoid abrupt changes into the next segment. The clouds in the scene are also moved every minute by incrementing their x position by 0.1.
Hot Air Balloons
The hot hair balloons were modeled in magicavoxel and then uploaded into the scene with the MTLLoader and OBJLoader. There are 8 unique models and each is cloned 3 times to fill up the scene. These are placed randomly in the sky via their x, y z coordinates. They fade into the scene at hour 9 using the opacity property of their material and take advantage of the mist to slightly hide the fade-in effect and fade-out at hour 19. Their x and y positions are changed every few seconds to mimic continuous movement in the scene.
Fireflies
Between hour 19 and hour 5, the fireflies appear in the scene. Initially, we had accompanied each firefly mesh with a point light but this made the scene run extremely slowly so these were removed.These fireflies are procedurally generated using three.js' points to create small particles in the scene. We use a custom shader with a yellow color and a sine wave function with the time as the parameter to update its opacity and glowing effect. These fireflies are positioned randomly over a restricted area and move them around by updating their x, y, z positions per frame.
Aurora
At midnight (hour 24) the aurora appears and stays until hour 3 in the night. We place a large plane geometry in the sky and subdivide it to smoothly deform it. We use a shader material that use both vertex and fragment shaders to control the appearance of the aurora. The vertex shader helps create a wave using a sine wave function with a time parameter to create a wavy effect along the x-axis of the plane. We then use the fragment shader to blend two colors, blue and green for the color of the aurora. Once the aurora appears in the scene, we add an ambient light matching the color of the aurora to illuminate the scene.
Some Cool Screenshots
Aurora implemented:
Glowing berries as light sources:
Balloons:
User Guide
The instructions below allow you to interact with the live demo of the project.
- Visit the Live Demo Site.
- The scene opens showing the scenery adapted to your local time
- You may use the direction keys on your keyboard or the A,W,S,D keys to navigate the scene.
- On the upper right side of the page, there are some simulation controls.
- Press play on the controls to see the scene cycle through different day segments. You can use the slider to change the speed of the simulation
- You may also pause the simulation when it is run.
- Click the reset button to return the scene to your local time.
Visual References
Below are some references that guided the design for our scene
Inspiration tree from Minecraft
Staxel 3d voxel grass
Sample Scene Inspiration from Magicavoxel page
Resources
- Texturepack used here :3