UNPKG

@matematrolii/sketchbook

Version:

3D matematrolii playground built on three.js and cannon.js

145 lines (118 loc) 3.88 kB
import { SkyShader } from "../../lib/shaders/SkyShader"; import * as THREE from "three"; import { World } from "./World"; import { IUpdatable } from "../interfaces/IUpdatable"; import { default as CSM } from "three-csm"; export class Sky extends THREE.Object3D implements IUpdatable { public updateOrder: number = 5; public sunPosition: THREE.Vector3 = new THREE.Vector3(); public csm: CSM; set theta(value: number) { this._theta = value; this.refreshSunPosition(); } set phi(value: number) { this._phi = value; this.refreshSunPosition(); this.refreshHemiIntensity(); } private _phi: number = 50; private _theta: number = 145; private hemiLight: THREE.HemisphereLight; private maxHemiIntensity: number = 0.9; private minHemiIntensity: number = 0.3; private skyMesh: THREE.Mesh; private skyMaterial: THREE.ShaderMaterial; private world: World; constructor(world: World) { super(); this.world = world; // Sky material this.skyMaterial = new THREE.ShaderMaterial({ uniforms: THREE.UniformsUtils.clone(SkyShader.uniforms), fragmentShader: SkyShader.fragmentShader, vertexShader: SkyShader.vertexShader, side: THREE.BackSide, }); // Mesh this.skyMesh = new THREE.Mesh( new THREE.SphereBufferGeometry(300, 24, 12), this.skyMaterial ); this.attach(this.skyMesh); // Ambient light this.hemiLight = new THREE.HemisphereLight(0xffffff, 0xffffff, 1.0); this.refreshHemiIntensity(); this.hemiLight.color.setHSL(0.59, 0.4, 0.6); this.hemiLight.groundColor.setHSL(0.095, 0.2, 0.75); this.hemiLight.position.set(0, 50, 0); this.world.graphicsWorld.add(this.hemiLight); // Legacy let splitsCallback = (amount, near, far) => { let arr = []; for (let i = amount - 1; i >= 0; i--) { arr.push(Math.pow(1 / 4, i)); } return arr; }; this.csm = new CSM({ fov: 80, far: world.camera.far, // maxFar lightIntensity: 0.4, cascades: 0, shadowMapSize: 2048, camera: world.camera, parent: world.graphicsWorld, mode: "uniform", //customSplitsCallback: splitsCallback, }); this.csm.fade = true; this.refreshSunPosition(); world.graphicsWorld.add(this); world.registerUpdatable(this); } public update(timeScale: number): void { this.position.copy(this.world.camera.position); this.refreshSunPosition(); this.csm.update(this.world.camera.matrix); this.csm.lightDirection = new THREE.Vector3( -this.sunPosition.x, -this.sunPosition.y, -this.sunPosition.z ).normalize(); } public refreshSunPosition(): void { const sunDistance = 10; this.sunPosition.x = sunDistance * Math.sin((this._theta * Math.PI) / 180) * Math.cos((this._phi * Math.PI) / 180); this.sunPosition.y = sunDistance * Math.sin((this._phi * Math.PI) / 180); this.sunPosition.z = sunDistance * Math.cos((this._theta * Math.PI) / 180) * Math.cos((this._phi * Math.PI) / 180); this.skyMaterial.uniforms.sunPosition.value.copy(this.sunPosition); this.skyMaterial.uniforms.cameraPos.value.copy(this.world.camera.position); } public refreshHemiIntensity(): void { this.hemiLight.intensity = this.minHemiIntensity + Math.pow(1 - Math.abs(this._phi - 90) / 90, 0.25) * (this.maxHemiIntensity - this.minHemiIntensity); } public destroy() { this.csm.remove(); this.csm = undefined; this.hemiLight = undefined; this.sunPosition = undefined; this._phi = undefined; this._theta = undefined; this.maxHemiIntensity = undefined; this.minHemiIntensity = undefined; this.skyMesh = undefined; this.skyMaterial = undefined; this.world = undefined; this.updateOrder = undefined; } }