2D Spherical Rotations in any Direction From Image
This shader allows you to get a UV mapped png and turn it into a sphere that rotates in a 2D engine! It took me a long time to make, and large credits to alxl, who wrote something similar and I took inspiration from. The key difference is that my code uses quaternions, so the sphere can now rotate in different directions instead of just one.
Here’s an example script that worked for me; the UV map I used had an aspect ratio of 2:1. Simply add the shader on a Sprite2D and it should work!
extends Sprite2D
var current_rotation: Quaternion = Quaternion.IDENTITY
var accumulated_rotation: Quaternion = Quaternion.IDENTITY
var rotationSpeed = 0.1
var shader_material: ShaderMaterial
var xAxis = Vector3(1.0, 0.0, 0.0)
var velocity = Vector2.ZERO
func _ready():
# Ensure we are working with a ShaderMaterial
shader_material = material as ShaderMaterial
if shader_material == null:
print("Error: No ShaderMaterial found on Sprite2D.")
else:
# Initialize shader parameter
shader_material.set_shader_parameter("quaternion", current_rotation)
func _process(delta: float) -> void:
velocity = Vector2.ZERO
var xRotation: float = 0.0
var yRotation: float = 0.0
# Handle input for rotation
if Input.is_action_pressed("up"):
velocity.y = -100 * delta
if Input.is_action_pressed("down"):
velocity.y = 100 * delta
if Input.is_action_pressed("left"):
velocity.x = -200 * delta
if Input.is_action_pressed("right"):
velocity.x = 100 * delta
if velocity.length() > 0:
velocity = velocity
position += velocity
xRotation = velocity.y * rotationSpeed
yRotation = -velocity.x * rotationSpeed
var xQuaternion: Quaternion = Quaternion(Vector3(1, 0, 0), xRotation)
var yQuaternion: Quaternion = Quaternion(Vector3(0, 1, 0), yRotation)
current_rotation = current_rotation * xQuaternion * yQuaternion
current_rotation = current_rotation.normalized()
if shader_material:
shader_material.set_shader_parameter("quaternion", current_rotation)
Shader code
shader_type canvas_item;
uniform vec4 quaternion = vec4(0.0, 0.0, 0.0, 1.0); // Default: identity quaternion
// Function to rotate a vector using a quaternion
vec3 rotate_with_quaternion(vec3 v, vec4 q) {
vec3 t = 2.0 * cross(q.xyz, v);
return v + q.w * t + cross(q.xyz, t);
}
void fragment() {
// Map UVs to normalized coordinates (-1, 1)
float px = 2.0 * (UV.x - 0.5);
float py = 2.0 * (UV.y - 0.5);
if (px * px + py * py > 1.0) {
COLOR.a = 0.0; // Outside of sphere
} else {
// Map to a 3D unit sphere
float pz = sqrt(max(0.0, 1.0 - px * px - py * py));
vec3 sphere_pos = vec3(px, py, pz);
// Apply quaternion rotation
sphere_pos = rotate_with_quaternion(sphere_pos, quaternion);
// Convert rotated position to UV coordinates
float theta = atan(sphere_pos.z, sphere_pos.x);
float phi = acos(clamp(sphere_pos.y, -1.0, 1.0));
// Map spherical coordinates to UV space
vec2 spherical_uv = vec2(0.5 + theta / (2.0 * PI), phi / PI);
// Sample texture using spherical coordinates
COLOR = texture(TEXTURE, spherical_uv);
}
}
Any way use in pixel style art? Make the border of sphere pixelated.