import { Colordx, colordx } from "@colordx/core" // constants from Hillaire, section 4 table 1 const RAYLEIGH_SCATTER = [5.802e-6, 13.558e-6, 33.1e-6] // rgb const MIE_SCATTER = 3.996e-6 const MIE_ABSORB = 4.44e-6 const OZONE_SCATTER = 0 const OZONE_ABSORB = [0.65e-6, 1.881e-6, 0.085e-6] // rgb const GROUND_ALBEDO = 0.3 const RAYLEIGH_SCALE_HEIGHT = 8e3 // metres const MIE_SCALE_HEIGHT = 1.2e3 // metres const GROUND_RADIUS = 6_360e3 // radius of the earth, metres const TOP_RADIUS = 6_460e3 // radius of the atmosphere, metres type Vec3 = [number, number, number]; function dot(v1: Vec3, v2: Vec3) { return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; } // ACES tonemapper (Knarkowicz) function aces(colour: Colordx): Colordx { Object.keys(colour.toRgb()).forEach((_key, colour) => { const n = colour * (2.51 * colour + 0.03) const d = colour * (2.43 * colour + 0.59) + 0.14 return Math.max(0, Math.min(1, n / d)) }) return colordx(colour) } function rayleighPhase(angle: number): number { return (3 * (1 + Math.cos(angle) ** 2)) / (16 * Math.PI); } function miePhase(angle: number): number { const g = 0.8; const scale = 3 / (8 * Math.PI); const num = (1 - g ** 2) * (1 + Math.cos(angle) ** 2); const denom = (2 + g ** 2) * (1 + g ** 2 - 2 * g * Math.cos(angle)) ** (3 / 2); return (scale * num) / denom; } // From "5.3.2 Intersecting Ray or Segment Against Sphere" (Real-Time Collision Detection) function intersectSphere(rayOrigin: Vec3, direction: Vec3, radius: number): number | null { // Sphere center is at origin (= 0), so rayOrigin - sphereCenter = rayOrigin const b = dot(rayOrigin, direction); const c = dot(rayOrigin, rayOrigin) - (radius ** 2); const discr = (b ** 2) - c; if (discr < 0) // Ray misses sphere return null; const t = -b - Math.sqrt(discr); if (t < 0) // Ray inside sphere; Use far discriminant return -b + Math.sqrt(discr); return t; }