import { Points, BufferGeometry, BufferAttribute, RawShaderMaterial, AdditiveBlending } from 'three'; import MathEx from 'js-util/MathEx'; import vs from './glsl/FireBallPoints.vs'; import fs from './glsl/FireBallPoints.fs'; const DURATION = 4; const NUM = 360; const DURATION_SHOW = 2; const DURATION_HIDE = 1; export default class FireBallPoints extends Points { constructor() { // Define Geometry const geometry = new BufferGeometry(); // Define attributes of the geometry const baPositions = new BufferAttribute(new Float32Array(NUM * 3), 3); const baDelays = new BufferAttribute(new Float32Array(NUM), 1); const baStartY = new BufferAttribute(new Float32Array(NUM), 1); for (var i = 0, ul = NUM; i < ul; i++) { const radian = MathEx.radians(Math.random() * 360); const radius = Math.random() * 2 + 1; baPositions.setXYZ( i, Math.cos(radian) * radius, 0, Math.sin(radian) * radius ); baDelays.setX(i, Math.random() * DURATION); baStartY.setX(i, Math.random() * 1); } geometry.setAttribute('position', baPositions); geometry.setAttribute('delay', baDelays); geometry.setAttribute('startY', baStartY); // Define Material const material = new RawShaderMaterial({ uniforms: { time: { value: 0 }, duration: { value: DURATION }, noiseTex: { value: null }, alphaShow: { value: 0 }, alphaHide: { value: 0 } }, vertexShader: vs, fragmentShader: fs, transparent: true, blending: AdditiveBlending, depthWrite: false }); // Create Object3D super(geometry, material); this.name = 'FireBallPoints'; } start(tex) { const { noiseTex } = this.material.uniforms; noiseTex.value = tex; } update(t, ts, th) { const { time, alphaShow, alphaHide } = this.material.uniforms; time.value += t; alphaShow.value = MathEx.clamp(ts / DURATION_SHOW, 0, 1); alphaHide.value = MathEx.clamp(th / DURATION_HIDE, 0, 1); this.rotation.set(0, time.value * 0.2, 0); } }