How to use multiple sky textures for the skybox?

Godot Version

Godot Engine v4.3.stable.mono.official

Question

I’m trying to make a 3D dynamic sky using multiple panoramic HDR textures for eg. day, evening, night, and morning. However I’m confused as to how I can implement such a sky.

Ideally I’d have an in-game variable representing the time in some autoloaded script, and rotate the global DirectionalLight3D as time passes, and the rotation of the light would determine which sky settings and texture to use. For example an evening sky (near the end of the in-game day) would produce warm orange light, and would use a specific evening sky texture or maybe even a sunset sky, while a night sky would contain many stars and galaxies and have a bluish-purple color.

I’ve tried writing a sky shader to at least make a sky which contains 2 textures (day and night), on opposite sides of each other, and with blended colors so the sky isn’t cleanly cut in half, but I have never written any shaders so I couldn’t figure it out.

I’ve also tried searching online for my issue but I haven’t found anyone trying to do what I do. From what I’ve seen, most people just use the PhysicalSkyMaterial and rotate the DirectionalLight3D, producing different sky colors, but never different sky textures.

You can blend your cube maps by hand depending on the time of day.

By “blend manually”, do you mean I need to edit the images in an external program to make them blend seamlessly, or use shaders to smoothly blend between them?

I currently have a few HDRI/equirectangular skybox textures and I was wondering if there was a standard way of doing this, as I see plenty of games have completely different skies (not just a color shift) depending on time of day. Is it all just advanced shaders?

There are different approaches you could take. As you already pointed out, you could just use the default physical sky shader for example, as it does the morning/day/night transition mostly on its own already. It already has an option to add a night sky texture and the shader could be further modified to fit your needs.

You also could create an entirely custom sky shader and blend between your skybox textures as you please. This would work with visual shaders (if you prefer the visual graph editor) and regular shader code. Take a look at the default PanoramaSkyMaterial shader for reference:

// NOTE: Shader automatically converted from Godot Engine 4.4.dev.mono's PanoramaSkyMaterial.

shader_type sky;

uniform sampler2D source_panorama : filter_linear, source_color, hint_default_black;
uniform float exposure : hint_range(0, 128) = 1.0;

void sky() {
	COLOR = texture(source_panorama, SKY_COORDS).rgb * exposure;
}

It’s as simple as sampling a texture using the SKY_COORDS UV coordinates and multiplying it with the exposure parameter. To sample and blend 2 different sky textures, you could do something like this:

shader_type sky;

uniform sampler2D tex_day : filter_linear, source_color, hint_default_black;
uniform sampler2D tex_night : filter_linear, source_color, hint_default_black;
uniform float exposure : hint_range(0, 128) = 1.0;
uniform float blend_value : hint_range(0, 1) = 0.0;

void sky() {
	COLOR = mix(texture(tex_day, SKY_COORDS).rgb * exposure, texture(tex_night, SKY_COORDS).rgb * exposure, blend_value);
}

You’d have to control the blend_value from that example by script then though, depending on your current time of day. But you probably already have a script controlling the rotation of the sun/your directional light, so you can simply adjust that to also control your sky shader.

2 Likes