Skybox from 6 textures

Skybox is the most efficient way to map a 360-degree sphere.
Just provide the six textures corresponding to the skybox faces.

Shader code
shader_type sky;

uniform sampler2D front : source_color;
uniform sampler2D back : source_color;
uniform sampler2D left : source_color;
uniform sampler2D right : source_color;
uniform sampler2D top : source_color;
uniform sampler2D bottom : source_color;

void sky() {
	COLOR = EYEDIR;
	vec3 absvec = abs(EYEDIR);
	float ma = max(max(absvec.x, absvec.y), absvec.z);
	vec3 dir = EYEDIR / (ma + 0.0001);
	
	if (EYEDIR.x >= ma) {
		COLOR = texture(left, dir.zy * vec2(-0.5, -0.5) - 0.5).rgb;
	}
	if (EYEDIR.x <= -ma) {
		COLOR = texture(right, dir.zy * vec2(0.5, -0.5) - 0.5).rgb;
	}
	if (EYEDIR.z >= ma) {
		COLOR = texture(front, dir.xy * vec2(0.5, -0.5) - 0.5).rgb;
	}
	if (EYEDIR.z <= -ma) {
		COLOR = texture(back, dir.xy * vec2(-0.5, -0.5) - 0.5).rgb;
	}
	if (EYEDIR.y >= ma) {
		COLOR = texture(top, dir.xz * vec2(-0.5, -0.5) + 0.5).rgb;
	}
	if (EYEDIR.y <= -ma) {
		COLOR = texture(bottom, dir.xz * vec2(-0.5, 0.5) + 0.5).rgb;
	}
}
Tags
skybox
The shader code and all code snippets in this post are under CC0 license and can be used freely without the author's permission. Images and videos, and assets depicted in those, do not fall under this license. For more info, see our License terms.

Related shaders

Sokpop Skybox

N64 Style Skybox

Stationary star skybox 3D

Subscribe
Notify of
guest

3 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Akeem29
Akeem29
1 month ago

I get tiny yet noticeable seams between the skybox textures, by doing some tests I realized that for each square texture, some amount of color from one side leaks to the other side. Changing “Fix alpha border” property of imported images had no effect on seams. How can those be fixed? Tested on Godot Engine v4.3.stable.official [77dcf97d8]

UPD: I found out that the seams can be reduced by changing:
uniform sampler2D front : source_color;
to:
uniform sampler2D front : source_color, filter_linear;
(for all 6 textures)
And completely removed by changing the same strings to:
uniform sampler2D front : source_color, filter_nearest;

But the first method still leaves very noticeable seams, while the latter deletes the seams but causes moire patterns to emerge

Last edited 1 month ago by Akeem29
Akeem29
Akeem29
1 month ago
Reply to  Akeem29

I found a way to completely remove the seams without causing moire patterns:

shader_type sky;

uniform sampler2D front : source_color, repeat_disable;
uniform sampler2D back : source_color, repeat_disable;
uniform sampler2D left : source_color, repeat_disable;
uniform sampler2D right : source_color, repeat_disable;
uniform sampler2D top : source_color, repeat_disable;
uniform sampler2D bottom : source_color, repeat_disable;

void sky() {
   COLOR = EYEDIR;
   vec3 absvec = abs(EYEDIR);
   float ma = max(max(absvec.x, absvec.y), absvec.z);
   vec3 dir = EYEDIR / (ma + 0.0001);

   if (EYEDIR.x >= ma) {
       COLOR = texture(left, dir.zy * vec2(-0.5, -0.5) + 0.5).rgb;
   }
   if (EYEDIR.x <= -ma) {
       COLOR = texture(right, dir.zy * vec2(0.5, -0.5) + 0.5).rgb;
   }
   if (EYEDIR.z >= ma) {
       COLOR = texture(front, dir.xy * vec2(0.5, -0.5) + 0.5).rgb;
   }
   if (EYEDIR.z <= -ma) {
       COLOR = texture(back, dir.xy * vec2(-0.5, -0.5) + 0.5).rgb;
   }
   if (EYEDIR.y >= ma) {
       COLOR = texture(top, dir.xz * vec2(-0.5, -0.5) + 0.5).rgb;
   }
   if (EYEDIR.y <= -ma) {
       COLOR = texture(bottom, dir.xz * vec2(-0.5, 0.5) + 0.5).rgb;
   }
}

Essentially I added repeat_disable to all textures and changed some coefficients from negative to positive for shader to work properly without repeating textures. I don’t have a lot of skybox textures to test this on, but on the one that I used this works seemingly perfect

jitspoe
jitspoe
30 days ago

In case anybody needs this, I converted it to a spatial shader for my needs (wanted to have the sky specifically draw on geometry that could be placed anywhere).

shader_type spatial;
render_mode unshaded;

varying vec3 world_position;


void vertex() {
    world_position = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
}


void fragment() {
    vec3 EYEDIR = normalize(world_position - CAMERA_POSITION_WORLD);
    vec3 absvec = abs(EYEDIR);
    float ma = max(max(absvec.x, absvec.y), absvec.z);
    vec3 dir = EYEDIR / (ma + 0.0001);

    if (EYEDIR.x >= ma) {
        ALBEDO = texture(left, dir.zy * vec2(-0.5, -0.5) - 0.5).rgb;
    }
    else if (EYEDIR.x <= -ma) {
        ALBEDO = texture(right, dir.zy * vec2(0.5, -0.5) - 0.5).rgb;
    }
    else if (EYEDIR.z >= ma) {
        ALBEDO = texture(front, dir.xy * vec2(0.5, -0.5) - 0.5).rgb;
    }
    else if (EYEDIR.z <= -ma) {
        ALBEDO = texture(back, dir.xy * vec2(-0.5, -0.5) - 0.5).rgb;
    }
    else if (EYEDIR.y >= ma) {
        ALBEDO = texture(top, dir.xz * vec2(0.5, 0.5) + 0.5).rgb;
    }
    else if (EYEDIR.y <= -ma) {
        ALBEDO = texture(bottom, dir.xz * vec2(0.5, -0.5) + 0.5).rgb;
    }
}

Also, I had to flip the top and bottom images as the program I used to generate them didn’t work with the original shader code, so tweak those vec2’s as needed, I guess.