import { BackSide, DirectionalLight, Mesh, Object3D, ShaderMaterial, SphereGeometry, Vector3, } from 'three'; import { plainText as vertexShader } from './shaders/atmosphere.vert'; import { plainText as fragmentShader } from './shaders/atmosphere.frag'; export class Atmosphere extends Object3D { public Rayleigh = 0.0025; public Mie = 0.0005; public Exposure = 15.0; public Scale = 1; public ScaleDepth = 0.25; public G = -0.95; public Wavelength = new Vector3(0.65, 0.57, 0.475); public geom?: SphereGeometry; public mesh?: Mesh; public shader = new ShaderMaterial({ vertexShader, fragmentShader, side: BackSide, transparent: true, }); constructor(public innerRadius: number, public outerRadius: number) { super(); this.Scale = 1 / (this.outerRadius - this.innerRadius); this.setUniforms(this.shader); this.initialize(); } set planetRadius(radius: number) { const thickness = this.thickness; this.innerRadius = radius; this.outerRadius = radius + thickness; this.refreshScale(); } get planetRadius() { return this.innerRadius; } set thickness(thickness: number) { this.outerRadius = thickness + this.innerRadius; this.refreshScale(); } get thickness() { return this.outerRadius - this.innerRadius; } setUniforms(shader: ShaderMaterial) { shader.uniforms.invWavelength = { value: [ 1 / Math.pow(this.Wavelength.x, 4), 1 / Math.pow(this.Wavelength.y, 4), 1 / Math.pow(this.Wavelength.z, 4), ], }; shader.uniforms.outerRadius = { value: this.outerRadius }; shader.uniforms.innerRadius = { value: this.innerRadius }; shader.uniforms.Kr = { value: this.Rayleigh }; shader.uniforms.Km = { value: this.Mie }; shader.uniforms.ESun = { value: this.Exposure }; shader.uniforms.scale = { value: this.Scale }; shader.uniforms.scaleDepth = { value: this.ScaleDepth }; shader.uniforms.g = { value: this.G }; } setLight(light: DirectionalLight) { this.shader.uniforms.lightDirection = { value: light.position.clone().normalize(), }; } initialize() { if (this.geom) { this.geom.dispose(); this.mesh && this.remove(this.mesh); } this.geom = new SphereGeometry(this.outerRadius, 128, 128); this.mesh = new Mesh(this.geom, this.shader); this.add(this.mesh); } private refreshScale() { this.Scale = 1 / (this.outerRadius - this.innerRadius); this.setUniforms(this.shader); this.initialize(); } }