N64 Style Skybox

Single-image skybox as seen in some N64 and PS1 games. I’ve always enjoyed this effect but found surprisingly little information about it, so I decided to recreate it myself.

This works as a spatial shader where you apply it to a skybox object surrounding the scene. It’s also possible to implement as a proper sky shader, but you have to pass the camera’s rotation from a script since sky shaders don’t have access to the view matrix.

Shader code
shader_type spatial;
render_mode unshaded;

uniform sampler2D sky_texture : source_color;
uniform bool lock_aspect = false;
uniform float aspect_ratio = 1.3333333;
uniform vec2 fov = vec2(180.0, 90.0);
uniform ivec2 tiling = ivec2(1, 1);
uniform vec2 offset = vec2(0.0, 0.0);

varying vec4 BG_COORDS;

void vertex() {
	//Camera YX rotation per Basis.get_euler source code
	float y = atan(VIEW_MATRIX[0][2], VIEW_MATRIX[2][2]);
	float x = asin(VIEW_MATRIX[1][2]);
	
	//Map rotation to screen space
	BG_COORDS.xy = vec2(y * -0.5, x) / PI;
	BG_COORDS.y += 0.5;
	
	BG_COORDS.w = fov.y / 180.0;
	BG_COORDS.z = !lock_aspect ? fov.x / 360.0 : VIEWPORT_SIZE.x / (VIEWPORT_SIZE.y * aspect_ratio) * BG_COORDS.w;
	
	//Keep background centered vertically when FOV changes
	BG_COORDS.y *= BG_COORDS.w > 1.0 ? 0.0 : 1.0 - BG_COORDS.w;
}

void fragment() {
	vec2 uv_offset = vec2(-offset.x, offset.y);
	vec2 uv = (SCREEN_UV + uv_offset) * BG_COORDS.zw + BG_COORDS.xy;
	uv *= vec2(tiling);
	ALBEDO = texture(sky_texture, uv).rgb;
}
Tags
N64, ps1, retro, sky, 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.

More from tentabrobpy

Local Screen Space UV

Surface Embedded Sprites

Related shaders

N64 Style Metallic

Skybox from 6 textures

3D Edge Detection Shader (Borderlands-Style)

Subscribe
Notify of
guest

14 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Peter
Peter
7 months ago

Can someone explain how to use it, i dont understand it

Purpbat
Purpbat
6 months ago
Reply to  tentabrobpy

could you offer the template texture you used in the gif? that looks very helpful incase you wanna make custom backgrounds

Elsenyh
5 months ago

Godot 3.5???

elvisish
3 months ago
Reply to  Elsenyh

shader_type spatial;
render_mode unshaded;

uniform sampler2D sky_texture : hint_albedo;
uniform bool lock_aspect = false;
uniform float aspect_ratio = 1.3333333;
uniform vec2 fov = vec2(180.0, 90.0);
//uniform ivec2 tiling = ivec2(1, 1);
uniform vec2 offset = vec2(0.0, 0.0);
const float PI = 3.14159;

varying vec2 BG_COORDS;
varying vec2 BG_SCALE;

void vertex() {
//Camera YX rotation per Basis.get_euler source code
float y = atan(CAMERA_MATRIX[0][2], CAMERA_MATRIX[2][2]);
float x = asin(CAMERA_MATRIX[1][2]);

//Map rotation to screen space
BG_COORDS = vec2(y * 0.5, -x) * -(1.0 / PI);
BG_COORDS.y += 0.5;

BG_SCALE.y = fov.y * (1.0 / 180.0);
BG_SCALE.x = !lock_aspect ? 
fov.x * (1.0 / 360.0) : 
VIEWPORT_SIZE.x / (VIEWPORT_SIZE.y * aspect_ratio) * BG_SCALE.y;

//Keep background centered vertically when FOV changes
BG_COORDS.y *= BG_SCALE.y > 1.0 ? 0.0 : 1.0 - BG_SCALE.y;
}

void fragment() {
vec2 uv_offset = vec2(-offset.x, offset.y);
vec2 uv = (SCREEN_UV + uv_offset) * BG_SCALE + BG_COORDS;
// uv *= vec2(tiling);
ALBEDO = texture(sky_texture, uv).rgb;
}


(I took tiling out cause it seems to break it)

Last edited 3 months ago by elvisish
Akeem
Akeem
5 months ago

This works perfectly, thank you! But I have a question – is it possible to use nearest neighbor interpolation instead of linear interpolation here?

elvisish
4 months ago
Reply to  Akeem

Replace:

uniform sampler2D sky_texture : source_color;

with:

uniform sampler2D sky_texture : source_color, filter_nearest;

Pan Jenkins
Pan Jenkins
3 months ago

Hey! nice shader. I have bug and i cannot use it.
https://imgur.com/a/pENKo6u

Pan Jenkins
3 months ago
Reply to  tentabrobpy

Thank you!!! it workssssss

Calinou
3 months ago

It’s also possible to implement as a proper sky shader, but you have to pass the camera’s rotation from a script since sky shaders don’t have access to the view matrix.

What about EYEDIR? It’s available in sky shaders (but not spatial shaders, interestingly).

REFORMED NIKO
REFORMED NIKO
3 months ago

AW HELL NAW PALESTINES BEING INVADED BY CARTOONS NOW?!????