|
@@ -0,0 +1,522 @@
|
|
|
+import * as THREE from 'https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js';
|
|
|
+
|
|
|
+import {OrbitControls} from 'https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/controls/OrbitControls.js';
|
|
|
+
|
|
|
+
|
|
|
+const _VS = `
|
|
|
+uniform float pointMultiplier;
|
|
|
+
|
|
|
+attribute float size;
|
|
|
+attribute float angle;
|
|
|
+attribute float blend;
|
|
|
+attribute vec4 colour;
|
|
|
+
|
|
|
+varying vec4 vColour;
|
|
|
+varying vec2 vAngle;
|
|
|
+varying float vBlend;
|
|
|
+
|
|
|
+void main() {
|
|
|
+ vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
|
|
|
+
|
|
|
+ gl_Position = projectionMatrix * mvPosition;
|
|
|
+ gl_PointSize = size * pointMultiplier / gl_Position.w;
|
|
|
+
|
|
|
+ vAngle = vec2(cos(angle), sin(angle));
|
|
|
+ vColour = colour;
|
|
|
+ vBlend = blend;
|
|
|
+}`;
|
|
|
+
|
|
|
+const _FS = `
|
|
|
+
|
|
|
+uniform sampler2D diffuseTexture;
|
|
|
+
|
|
|
+varying vec4 vColour;
|
|
|
+varying vec2 vAngle;
|
|
|
+varying float vBlend;
|
|
|
+
|
|
|
+void main() {
|
|
|
+ vec2 coords = (gl_PointCoord - 0.5) * mat2(vAngle.x, vAngle.y, -vAngle.y, vAngle.x) + 0.5;
|
|
|
+ gl_FragColor = texture2D(diffuseTexture, coords) * vColour;
|
|
|
+ gl_FragColor.xyz *= gl_FragColor.w;
|
|
|
+ gl_FragColor.w *= vBlend;
|
|
|
+}`;
|
|
|
+
|
|
|
+
|
|
|
+const _VS_2 = `
|
|
|
+uniform float pointMultiplier;
|
|
|
+
|
|
|
+attribute float size;
|
|
|
+attribute float angle;
|
|
|
+attribute vec4 colour;
|
|
|
+
|
|
|
+varying vec4 vColour;
|
|
|
+varying vec2 vAngle;
|
|
|
+
|
|
|
+void main() {
|
|
|
+ vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
|
|
|
+
|
|
|
+ gl_Position = projectionMatrix * mvPosition;
|
|
|
+ gl_PointSize = size * pointMultiplier / gl_Position.w;
|
|
|
+
|
|
|
+ vAngle = vec2(cos(angle), sin(angle));
|
|
|
+ vColour = colour;
|
|
|
+}`;
|
|
|
+
|
|
|
+const _FS_2 = `
|
|
|
+
|
|
|
+uniform sampler2D diffuseTexture;
|
|
|
+
|
|
|
+varying vec4 vColour;
|
|
|
+varying vec2 vAngle;
|
|
|
+
|
|
|
+void main() {
|
|
|
+ vec2 coords = (gl_PointCoord - 0.5) * mat2(vAngle.x, vAngle.y, -vAngle.y, vAngle.x) + 0.5;
|
|
|
+ gl_FragColor = texture2D(diffuseTexture, coords) * vColour;
|
|
|
+}`;
|
|
|
+
|
|
|
+
|
|
|
+class LinearSpline {
|
|
|
+ constructor(lerp) {
|
|
|
+ this._points = [];
|
|
|
+ this._lerp = lerp;
|
|
|
+ }
|
|
|
+
|
|
|
+ AddPoint(t, d) {
|
|
|
+ this._points.push([t, d]);
|
|
|
+ }
|
|
|
+
|
|
|
+ Get(t) {
|
|
|
+ let p1 = 0;
|
|
|
+
|
|
|
+ for (let i = 0; i < this._points.length; i++) {
|
|
|
+ if (this._points[i][0] >= t) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ p1 = i;
|
|
|
+ }
|
|
|
+
|
|
|
+ const p2 = Math.min(this._points.length - 1, p1 + 1);
|
|
|
+
|
|
|
+ if (p1 == p2) {
|
|
|
+ return this._points[p1][1];
|
|
|
+ }
|
|
|
+
|
|
|
+ return this._lerp(
|
|
|
+ (t - this._points[p1][0]) / (
|
|
|
+ this._points[p2][0] - this._points[p1][0]),
|
|
|
+ this._points[p1][1], this._points[p2][1]);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+class ParticleSystem {
|
|
|
+ constructor(params) {
|
|
|
+ const uniforms = {
|
|
|
+ diffuseTexture: {
|
|
|
+ value: new THREE.TextureLoader().load('./resources/fire.png')
|
|
|
+ },
|
|
|
+ pointMultiplier: {
|
|
|
+ value: window.innerHeight / (2.0 * Math.tan(0.5 * 60.0 * Math.PI / 180.0))
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ this._material = new THREE.ShaderMaterial({
|
|
|
+ uniforms: uniforms,
|
|
|
+ vertexShader: _VS,
|
|
|
+ fragmentShader: _FS,
|
|
|
+ blending: THREE.CustomBlending,
|
|
|
+ blendEquation: THREE.AddEquation,
|
|
|
+ blendSrc: THREE.OneFactor,
|
|
|
+ blendDst: THREE.OneMinusSrcAlphaFactor,
|
|
|
+ depthTest: true,
|
|
|
+ depthWrite: false,
|
|
|
+ transparent: true,
|
|
|
+ vertexColors: true
|
|
|
+ });
|
|
|
+
|
|
|
+ this._camera = params.camera;
|
|
|
+ this._particles = [];
|
|
|
+
|
|
|
+ this._geometry = new THREE.BufferGeometry();
|
|
|
+ this._geometry.setAttribute('position', new THREE.Float32BufferAttribute([], 3));
|
|
|
+ this._geometry.setAttribute('size', new THREE.Float32BufferAttribute([], 1));
|
|
|
+ this._geometry.setAttribute('colour', new THREE.Float32BufferAttribute([], 4));
|
|
|
+ this._geometry.setAttribute('angle', new THREE.Float32BufferAttribute([], 1));
|
|
|
+ this._geometry.setAttribute('blend', new THREE.Float32BufferAttribute([], 1));
|
|
|
+
|
|
|
+ this._points = new THREE.Points(this._geometry, this._material);
|
|
|
+
|
|
|
+ params.parent.add(this._points);
|
|
|
+
|
|
|
+ // Declare a few splines for different sets of particles. This isn't structured that well, we should
|
|
|
+ // instead separate these out into new particle system instances with customizable parameters. But
|
|
|
+ // for the purposes of a demo, more than enough.
|
|
|
+ this._alphaSplineF = new LinearSpline((t, a, b) => {
|
|
|
+ return a + t * (b - a);
|
|
|
+ });
|
|
|
+ this._alphaSplineF.AddPoint(0.0, 0.0);
|
|
|
+ this._alphaSplineF.AddPoint(0.1, 1.0);
|
|
|
+ this._alphaSplineF.AddPoint(0.5, 1.0);
|
|
|
+ this._alphaSplineF.AddPoint(1.0, 0.0);
|
|
|
+
|
|
|
+ this._colourSplineF = new LinearSpline((t, a, b) => {
|
|
|
+ const c = a.clone();
|
|
|
+ return c.lerp(b, t);
|
|
|
+ });
|
|
|
+ this._colourSplineF.AddPoint(0.0, new THREE.Color(0xFFFF80));
|
|
|
+ this._colourSplineF.AddPoint(1.0, new THREE.Color(0xFF8080));
|
|
|
+
|
|
|
+ this._sizeSplineF = new LinearSpline((t, a, b) => {
|
|
|
+ return a + t * (b - a);
|
|
|
+ });
|
|
|
+ this._sizeSplineF.AddPoint(0.0, 1.0);
|
|
|
+ this._sizeSplineF.AddPoint(0.25, 7.0);
|
|
|
+ this._sizeSplineF.AddPoint(0.5, 2.5);
|
|
|
+ this._sizeSplineF.AddPoint(1.0, 0.0);
|
|
|
+
|
|
|
+ this._alphaSplineS = new LinearSpline((t, a, b) => {
|
|
|
+ return a + t * (b - a);
|
|
|
+ });
|
|
|
+ this._alphaSplineS.AddPoint(0.0, 0.0);
|
|
|
+ this._alphaSplineS.AddPoint(0.1, 1.0);
|
|
|
+ this._alphaSplineS.AddPoint(0.5, 1.0);
|
|
|
+ this._alphaSplineS.AddPoint(1.0, 0.0);
|
|
|
+
|
|
|
+ this._colourSplineS = new LinearSpline((t, a, b) => {
|
|
|
+ const c = a.clone();
|
|
|
+ return c.lerp(b, t);
|
|
|
+ });
|
|
|
+ this._colourSplineS.AddPoint(0.0, new THREE.Color(0x202020));
|
|
|
+ this._colourSplineS.AddPoint(1.0, new THREE.Color(0x000000));
|
|
|
+
|
|
|
+ this._sizeSplineS = new LinearSpline((t, a, b) => {
|
|
|
+ return a + t * (b - a);
|
|
|
+ });
|
|
|
+ this._sizeSplineS.AddPoint(0.0, 1.0);
|
|
|
+ this._sizeSplineS.AddPoint(0.5, 8.0);
|
|
|
+ this._sizeSplineS.AddPoint(1.0, 16.0);
|
|
|
+
|
|
|
+ this._alphaSplineX = new LinearSpline((t, a, b) => {
|
|
|
+ return a + t * (b - a);
|
|
|
+ });
|
|
|
+ this._alphaSplineX.AddPoint(0.0, 0.0);
|
|
|
+ this._alphaSplineX.AddPoint(0.1, 1.0);
|
|
|
+ this._alphaSplineX.AddPoint(0.9, 1.0);
|
|
|
+ this._alphaSplineX.AddPoint(1.0, 0.0);
|
|
|
+
|
|
|
+ this._colourSplineX = new LinearSpline((t, a, b) => {
|
|
|
+ const c = a.clone();
|
|
|
+ return c.lerp(b, t);
|
|
|
+ });
|
|
|
+ this._colourSplineX.AddPoint(0.0, new THREE.Color(0xFF8080));
|
|
|
+ this._colourSplineX.AddPoint(1.0, new THREE.Color(0xFFFFFF));
|
|
|
+
|
|
|
+ this._sizeSplineX = new LinearSpline((t, a, b) => {
|
|
|
+ return a + t * (b - a);
|
|
|
+ });
|
|
|
+ this._sizeSplineX.AddPoint(0.0, 1.0);
|
|
|
+ this._sizeSplineX.AddPoint(1.0, 1.0);
|
|
|
+
|
|
|
+ this._rateLimiter = 0.0;
|
|
|
+
|
|
|
+ this._UpdateGeometry();
|
|
|
+ }
|
|
|
+
|
|
|
+ _CreateParticleF() {
|
|
|
+ const life = (Math.random() * 0.75 + 0.25) * 10.0;
|
|
|
+ return {
|
|
|
+ position: new THREE.Vector3(
|
|
|
+ (Math.random() * 2 - 1) * 4.0,
|
|
|
+ (Math.random() * 2 - 1) * 4.0,
|
|
|
+ (Math.random() * 2 - 1) * 4.0),
|
|
|
+ size: (Math.random() * 0.5 + 0.5) * 2.0,
|
|
|
+ colour: new THREE.Color(),
|
|
|
+ alpha: 1.0,
|
|
|
+ life: life,
|
|
|
+ maxLife: life,
|
|
|
+ rotation: Math.random() * 2.0 * Math.PI,
|
|
|
+ velocity: new THREE.Vector3(0, 5, 0),
|
|
|
+ blend: 0.0,
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ _CreateParticleX() {
|
|
|
+ const life = (Math.random() * 0.75 + 0.25) * 2.0;
|
|
|
+ const dirX = (Math.random() * 2.0 - 1.0) * 3.0;
|
|
|
+ const dirY = (Math.random() * 2.0 - 1.0) * 3.0;
|
|
|
+ return {
|
|
|
+ position: new THREE.Vector3(
|
|
|
+ (Math.random() * 2 - 1) * 4.0,
|
|
|
+ 10 + (Math.random() * 2 - 1) * 4.0,
|
|
|
+ (Math.random() * 2 - 1) * 4.0),
|
|
|
+ size: (Math.random() * 0.5 + 0.5) * 0.5,
|
|
|
+ colour: new THREE.Color(),
|
|
|
+ alpha: 1.0,
|
|
|
+ life: life,
|
|
|
+ maxLife: life,
|
|
|
+ rotation: Math.random() * 2.0 * Math.PI,
|
|
|
+ velocity: new THREE.Vector3(dirX, 10, dirY),
|
|
|
+ blend: 0.0,
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ _CreateParticleS() {
|
|
|
+ const life = (Math.random() * 0.75 + 0.25) * 15.0;
|
|
|
+ return {
|
|
|
+ position: new THREE.Vector3(
|
|
|
+ (Math.random() * 2 - 1) * 4.0,
|
|
|
+ 10 + (Math.random() * 2 - 1) * 4.0,
|
|
|
+ (Math.random() * 2 - 1) * 4.0),
|
|
|
+ size: (Math.random() * 0.5 + 0.5) * 2.0,
|
|
|
+ colour: new THREE.Color(),
|
|
|
+ alpha: 1.0,
|
|
|
+ life: life,
|
|
|
+ maxLife: life,
|
|
|
+ rotation: Math.random() * 2.0 * Math.PI,
|
|
|
+ velocity: new THREE.Vector3(0, 5, 0),
|
|
|
+ blend: 1.0,
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ _AddParticles(timeElapsed) {
|
|
|
+ this._rateLimiter += timeElapsed;
|
|
|
+ const n = Math.floor(this._rateLimiter * 120.0);
|
|
|
+ this._rateLimiter -= n / 120.0;
|
|
|
+
|
|
|
+ for (let i = 0; i < n; i++) {
|
|
|
+ const p = this._CreateParticleF();
|
|
|
+ this._particles.push(p);
|
|
|
+ }
|
|
|
+ for (let i = 0; i < n; i++) {
|
|
|
+ const p = this._CreateParticleS();
|
|
|
+ this._particles.push(p);
|
|
|
+ }
|
|
|
+ for (let i = 0; i < n * 2; i++) {
|
|
|
+ const p = this._CreateParticleX();
|
|
|
+ this._particles.push(p);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ _UpdateGeometry() {
|
|
|
+ const positions = [];
|
|
|
+ const sizes = [];
|
|
|
+ const colours = [];
|
|
|
+ const angles = [];
|
|
|
+ const blends = [];
|
|
|
+
|
|
|
+ const box = new THREE.Box3();
|
|
|
+ for (let p of this._particles) {
|
|
|
+ positions.push(p.position.x, p.position.y, p.position.z);
|
|
|
+ colours.push(p.colour.r, p.colour.g, p.colour.b, p.alpha);
|
|
|
+ sizes.push(p.currentSize);
|
|
|
+ angles.push(p.rotation);
|
|
|
+ blends.push(p.blend);
|
|
|
+
|
|
|
+ box.expandByPoint(p.position);
|
|
|
+ }
|
|
|
+
|
|
|
+ this._geometry.setAttribute(
|
|
|
+ 'position', new THREE.Float32BufferAttribute(positions, 3));
|
|
|
+ this._geometry.setAttribute(
|
|
|
+ 'size', new THREE.Float32BufferAttribute(sizes, 1));
|
|
|
+ this._geometry.setAttribute(
|
|
|
+ 'colour', new THREE.Float32BufferAttribute(colours, 4));
|
|
|
+ this._geometry.setAttribute(
|
|
|
+ 'angle', new THREE.Float32BufferAttribute(angles, 1));
|
|
|
+ this._geometry.setAttribute(
|
|
|
+ 'blend', new THREE.Float32BufferAttribute(blends, 1));
|
|
|
+
|
|
|
+ this._geometry.attributes.position.needsUpdate = true;
|
|
|
+ this._geometry.attributes.size.needsUpdate = true;
|
|
|
+ this._geometry.attributes.colour.needsUpdate = true;
|
|
|
+ this._geometry.attributes.angle.needsUpdate = true;
|
|
|
+ this._geometry.attributes.blend.needsUpdate = true;
|
|
|
+
|
|
|
+ this._geometry.boundingBox = box;
|
|
|
+ this._geometry.boundingSphere = new THREE.Sphere();
|
|
|
+ box.getBoundingSphere(this._geometry.boundingSphere);
|
|
|
+ }
|
|
|
+
|
|
|
+ _UpdateParticles(timeElapsed) {
|
|
|
+ for (let p of this._particles) {
|
|
|
+ p.life -= timeElapsed;
|
|
|
+ }
|
|
|
+
|
|
|
+ this._particles = this._particles.filter(p => {
|
|
|
+ return p.life > 0.0;
|
|
|
+ });
|
|
|
+
|
|
|
+ for (let p of this._particles) {
|
|
|
+ const t = 1.0 - p.life / p.maxLife;
|
|
|
+
|
|
|
+ p.rotation += timeElapsed * 0.5;
|
|
|
+
|
|
|
+ if (p.blend == 0.0) {
|
|
|
+ if (p.velocity.x != 0.0) {
|
|
|
+ p.alpha = this._alphaSplineX.Get(t);
|
|
|
+ p.currentSize = p.size * this._sizeSplineX.Get(t);
|
|
|
+ p.colour.copy(this._colourSplineX.Get(t));
|
|
|
+ } else {
|
|
|
+ p.alpha = this._alphaSplineF.Get(t);
|
|
|
+ p.currentSize = p.size * this._sizeSplineF.Get(t);
|
|
|
+ p.colour.copy(this._colourSplineF.Get(t));
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ p.alpha = this._alphaSplineS.Get(t);
|
|
|
+ p.currentSize = p.size * this._sizeSplineS.Get(t);
|
|
|
+ p.colour.copy(this._colourSplineS.Get(t));
|
|
|
+ }
|
|
|
+
|
|
|
+ p.position.add(p.velocity.clone().multiplyScalar(timeElapsed));
|
|
|
+
|
|
|
+ const drag = p.velocity.clone();
|
|
|
+ drag.multiplyScalar(timeElapsed * 0.1);
|
|
|
+ drag.x = Math.sign(p.velocity.x) * Math.min(Math.abs(drag.x), Math.abs(p.velocity.x));
|
|
|
+ drag.y = Math.sign(p.velocity.y) * Math.min(Math.abs(drag.y), Math.abs(p.velocity.y));
|
|
|
+ drag.z = Math.sign(p.velocity.z) * Math.min(Math.abs(drag.z), Math.abs(p.velocity.z));
|
|
|
+ p.velocity.sub(drag);
|
|
|
+ }
|
|
|
+
|
|
|
+ this._particles.sort((a, b) => {
|
|
|
+ const d1 = this._camera.position.distanceTo(a.position);
|
|
|
+ const d2 = this._camera.position.distanceTo(b.position);
|
|
|
+
|
|
|
+ if (d1 > d2) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (d1 < d2) {
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ Step(timeElapsed) {
|
|
|
+ this._AddParticles(timeElapsed);
|
|
|
+ this._UpdateParticles(timeElapsed);
|
|
|
+ this._UpdateGeometry();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+class BlendingDemo {
|
|
|
+ constructor() {
|
|
|
+ this._Initialize();
|
|
|
+ }
|
|
|
+
|
|
|
+ _Initialize() {
|
|
|
+ this._threejs = new THREE.WebGLRenderer({
|
|
|
+ antialias: true,
|
|
|
+ });
|
|
|
+ this._threejs.setPixelRatio(window.devicePixelRatio);
|
|
|
+ this._threejs.setSize(window.innerWidth, window.innerHeight);
|
|
|
+
|
|
|
+ document.body.appendChild(this._threejs.domElement);
|
|
|
+
|
|
|
+ window.addEventListener('resize', () => {
|
|
|
+ this._OnWindowResize();
|
|
|
+ }, false);
|
|
|
+
|
|
|
+
|
|
|
+ const loader = new THREE.CubeTextureLoader();
|
|
|
+ const texture = loader.load([
|
|
|
+ './resources/posx.jpg',
|
|
|
+ './resources/negx.jpg',
|
|
|
+ './resources/posy.jpg',
|
|
|
+ './resources/negy.jpg',
|
|
|
+ './resources/posz.jpg',
|
|
|
+ './resources/negz.jpg',
|
|
|
+ ]);
|
|
|
+
|
|
|
+ this._backgroundScene = new THREE.Scene();
|
|
|
+ this._backgroundScene.background = new THREE.Color(0x3f4f6a);
|
|
|
+
|
|
|
+ const fov = 60;
|
|
|
+ const aspect = 1920 / 1080;
|
|
|
+ const near = 1.0;
|
|
|
+ const far = 1000.0;
|
|
|
+ this._backgroundCamera = new THREE.PerspectiveCamera(fov, aspect, near, far);
|
|
|
+ this._backgroundCamera.position.set(35, 10, 0);
|
|
|
+ const controls = new OrbitControls(this._backgroundCamera, this._threejs.domElement);
|
|
|
+ controls.target.set(0, 10, 0);
|
|
|
+ controls.update();
|
|
|
+
|
|
|
+ this._camera = new THREE.OrthographicCamera(0, 1920, 1280, 0, 0, 1);
|
|
|
+
|
|
|
+ const mat = new THREE.MeshBasicMaterial({
|
|
|
+ map: new THREE.TextureLoader().load('./resources/fire.jpg'),
|
|
|
+ depthTest: true,
|
|
|
+ depthWrite: false,
|
|
|
+ transparent: true,
|
|
|
+ blending: THREE.CustomBlending,
|
|
|
+ blendEquation: THREE.AddEquation,
|
|
|
+ blendSrc: THREE.OneFactor,
|
|
|
+ blendDst: THREE.OneMinusSrcAlphaFactor,
|
|
|
+ });
|
|
|
+
|
|
|
+ const postPlane = new THREE.PlaneBufferGeometry(800, 800);
|
|
|
+ const postQuad = new THREE.Mesh(postPlane, mat);
|
|
|
+ postQuad.position.set(
|
|
|
+ 1920 - (800 * 0.5 + (1280 - 800) * 0.5),
|
|
|
+ 1280 * 0.5, 0);
|
|
|
+
|
|
|
+ this._scene = new THREE.Scene();
|
|
|
+ // this._scene.add(postQuad);
|
|
|
+
|
|
|
+ this._particles = new ParticleSystem({
|
|
|
+ parent: this._backgroundScene,
|
|
|
+ camera: this._backgroundCamera,
|
|
|
+ });
|
|
|
+
|
|
|
+ this._UpdateText();
|
|
|
+
|
|
|
+ this._previousRAF = null;
|
|
|
+ this._RAF();
|
|
|
+ }
|
|
|
+
|
|
|
+ _OnWindowResize() {
|
|
|
+ this._backgroundCamera.aspect = window.innerWidth / window.innerHeight;
|
|
|
+ this._backgroundCamera.updateProjectionMatrix();
|
|
|
+ this._camera.aspect = window.innerWidth / window.innerHeight;
|
|
|
+ this._camera.updateProjectionMatrix();
|
|
|
+ this._threejs.setSize(window.innerWidth, window.innerHeight);
|
|
|
+ }
|
|
|
+
|
|
|
+ _UpdateText() {
|
|
|
+ document.getElementById('overlay').style.display = 'none';
|
|
|
+ document.getElementById('func').innerText = 'Subtract';
|
|
|
+ document.getElementById('src').innerText = 'ONE';
|
|
|
+ document.getElementById('dst').innerText = 'ONE';
|
|
|
+ }
|
|
|
+
|
|
|
+ _RAF() {
|
|
|
+ requestAnimationFrame((t) => {
|
|
|
+ if (this._previousRAF === null) {
|
|
|
+ this._previousRAF = t;
|
|
|
+ }
|
|
|
+
|
|
|
+ this._Step(t - this._previousRAF);
|
|
|
+ this._threejs.autoClear = true;
|
|
|
+ this._threejs.render(this._backgroundScene, this._backgroundCamera);
|
|
|
+ this._threejs.autoClear = false;
|
|
|
+ this._threejs.render(this._scene, this._camera);
|
|
|
+ this._RAF();
|
|
|
+
|
|
|
+ this._previousRAF = t;
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ _Step(timeElapsed) {
|
|
|
+ const timeElapsedS = timeElapsed * 0.001;
|
|
|
+
|
|
|
+ this._particles.Step(timeElapsedS);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+let _APP = null;
|
|
|
+
|
|
|
+window.addEventListener('DOMContentLoaded', () => {
|
|
|
+ _APP = new BlendingDemo();
|
|
|
+});
|