# Writes a procedural equirectangular sky (blue gradient, haze at the horizon, a sun, a plain # ground) as a PNG for the test scenes; a real HDRI drops in the same way. # Usage: python3 util/makeSky.py testScripts/sky.png [width] import sys, math import numpy as np from PIL import Image out = sys.argv[1] W = int(sys.argv[2]) if len(sys.argv) > 2 else 1024 H = W // 2 v = (np.arange(H) + 0.5) / H u = (np.arange(W) + 0.5) / W theta = v * math.pi # 0 at the zenith phi = (u - 0.5) * 2 * math.pi # 0 faces -Z T, P = np.meshgrid(theta, phi, indexing='ij') y = np.cos(T) x = np.sin(T) * np.sin(P) z = -np.sin(T) * np.cos(P) img = np.zeros((H, W, 3)) # Sky: deep blue overhead to pale at the horizon. t = np.clip(y, 0, 1) sky = (1 - t)[..., None] * np.array([0.72, 0.80, 0.92]) + t[..., None] * np.array([0.20, 0.42, 0.85]) # Sun toward +X, high up, with a soft glow. sun = np.array([0.55, 0.65, -0.52]); sun /= np.linalg.norm(sun) cosang = x * sun[0] + y * sun[1] + z * sun[2] glow = np.exp((cosang - 1) * 60) * 1.5 + np.exp((cosang - 1) * 700) * 6.0 sky += glow[..., None] * np.array([1.0, 0.95, 0.85]) # Haze just above the horizon. haze = np.exp(-np.abs(y) * 12) * 0.35 sky += haze[..., None] * np.array([0.9, 0.85, 0.8]) # Ground: a muted olive that darkens straight down. g = np.clip(-y, 0, 1) ground = (1 - g)[..., None] * np.array([0.45, 0.42, 0.34]) + g[..., None] * np.array([0.22, 0.20, 0.16]) img = np.where((y >= 0)[..., None], sky, ground) # To sRGB bytes; the engine decodes PNG skies back to linear. img = np.clip(img, 0, 1) srgb = np.where(img <= 0.0031308, img * 12.92, 1.055 * np.power(img, 1 / 2.4) - 0.055) Image.fromarray((srgb * 255).astype(np.uint8)).save(out) print(out, W, H)