diff --git a/src/framework/components/particle-system/system.js b/src/framework/components/particle-system/system.js index cd3c696a022..9341300a214 100644 --- a/src/framework/components/particle-system/system.js +++ b/src/framework/components/particle-system/system.js @@ -2,8 +2,6 @@ import { Curve } from '../../../core/math/curve.js'; import { CurveSet } from '../../../core/math/curve-set.js'; import { Vec3 } from '../../../core/math/vec3.js'; -import { LIGHTTYPE_DIRECTIONAL } from '../../../scene/constants.js'; - import { Asset } from '../../asset/asset.js'; import { Component } from '../component.js'; @@ -205,6 +203,12 @@ class ParticleSystemComponentSystem extends ComponentSystem { const components = this.store; let numSteps; const stats = this.app.stats.particles; + const composition = this.app.scene.layers; + + // disable light cube on all layers first + for (let i = 0; i < composition.layerList.length; i++) { + composition.layerList[i].requiresLightCube = false; + } for (const id in components) { if (components.hasOwnProperty(id)) { @@ -216,36 +220,14 @@ class ParticleSystemComponentSystem extends ComponentSystem { const emitter = entity.particlesystem.emitter; if (!emitter?.meshInstance.visible) continue; - // Bake ambient and directional lighting into one ambient cube - // TODO: only do if lighting changed - // TODO: don't do for every emitter + // if emitter is using lighting, enable light cube on all layers it is assigned to if (emitter.lighting) { const layers = data.layers; - let lightCube; for (let i = 0; i < layers.length; i++) { - const layer = this.app.scene.layers.getLayerById(layers[i]); - if (!layer) continue; - - if (!layer._lightCube) { - layer._lightCube = new Float32Array(6 * 3); - } - lightCube = layer._lightCube; - for (let j = 0; j < 6; j++) { - lightCube[j * 3] = this.app.scene.ambientLight.r; - lightCube[j * 3 + 1] = this.app.scene.ambientLight.g; - lightCube[j * 3 + 2] = this.app.scene.ambientLight.b; - } - const dirs = layer._splitLights[LIGHTTYPE_DIRECTIONAL]; - for (let j = 0; j < dirs.length; j++) { - for (let c = 0; c < 6; c++) { - const weight = Math.max(emitter.lightCubeDir[c].dot(dirs[j]._direction), 0) * dirs[j]._intensity; - lightCube[c * 3] += dirs[j]._color.r * weight; - lightCube[c * 3 + 1] += dirs[j]._color.g * weight; - lightCube[c * 3 + 2] += dirs[j]._color.b * weight; - } - } + const layer = composition.getLayerById(layers[i]); + if (layer) + layer.requiresLightCube = true; } - emitter.constantLightCube.setValue(lightCube); // ? } if (!data.paused) { diff --git a/src/scene/graphics/light-cube.js b/src/scene/graphics/light-cube.js new file mode 100644 index 00000000000..c2596fe1506 --- /dev/null +++ b/src/scene/graphics/light-cube.js @@ -0,0 +1,49 @@ +import { Vec3 } from "../../core/math/vec3.js"; +import { LIGHTTYPE_DIRECTIONAL } from "../constants.js"; + +const lightCubeDir = [ + new Vec3(-1, 0, 0), + new Vec3(1, 0, 0), + new Vec3(0, -1, 0), + new Vec3(0, 1, 0), + new Vec3(0, 0, -1), + new Vec3(0, 0, 1) +]; + +/** + * A lighting cube represented by 6 colors, one per cube direction. Use for simple lighting on the + * particle system. + * + * @ignore + */ +class LightCube { + colors = new Float32Array(6 * 3); + + update(ambientLight, lights) { + const colors = this.colors; + + // ambient contribution + const { r, g, b } = ambientLight; + for (let j = 0; j < 6; j++) { + colors[j * 3] = r; + colors[j * 3 + 1] = g; + colors[j * 3 + 2] = b; + } + + // directional contribution + for (let j = 0; j < lights.length; j++) { + const light = lights[j]; + if (light._type === LIGHTTYPE_DIRECTIONAL) { + for (let c = 0; c < 6; c++) { + const weight = Math.max(lightCubeDir[c].dot(light._direction), 0) * light._intensity; + const lightColor = light._color; + colors[c * 3] += lightColor.r * weight; + colors[c * 3 + 1] += lightColor.g * weight; + colors[c * 3 + 2] += lightColor.b * weight; + } + } + } + } +} + +export { LightCube }; diff --git a/src/scene/layer.js b/src/scene/layer.js index 00f717e2226..62ea57891a5 100644 --- a/src/scene/layer.js +++ b/src/scene/layer.js @@ -136,6 +136,11 @@ class Layer { */ _clusteredLightsSet = new Set(); + /** + * True if the objects rendered on the layer require light cube (emitters with lighting do). + */ + requiresLightCube = false; + /** * Create a new Layer instance. * @@ -412,12 +417,6 @@ class Layer { // #endif this._shaderVersion = -1; - - /** - * @type {Float32Array|null} - * @ignore - */ - this._lightCube = null; } /** diff --git a/src/scene/particle-system/particle-emitter.js b/src/scene/particle-system/particle-emitter.js index 3fd788fcbbf..7ce356552b7 100644 --- a/src/scene/particle-system/particle-emitter.js +++ b/src/scene/particle-system/particle-emitter.js @@ -287,7 +287,6 @@ class ParticleEmitter { this._gpuUpdater = new ParticleGPUUpdater(this, gd); this._cpuUpdater = new ParticleCPUUpdater(this); - this.constantLightCube = gd.scope.resolve('lightCube[0]'); this.emitterPosUniform = new Float32Array(3); this.wrapBoundsUniform = new Float32Array(3); this.emitterScaleUniform = new Float32Array([1, 1, 1]); @@ -314,15 +313,6 @@ class ParticleEmitter { setProperty('radialSpeedGraph', default0Curve); setProperty('radialSpeedGraph2', this.radialSpeedGraph); - this.lightCube = new Float32Array(6 * 3); - this.lightCubeDir = new Array(6); - this.lightCubeDir[0] = new Vec3(-1, 0, 0); - this.lightCubeDir[1] = new Vec3(1, 0, 0); - this.lightCubeDir[2] = new Vec3(0, -1, 0); - this.lightCubeDir[3] = new Vec3(0, 1, 0); - this.lightCubeDir[4] = new Vec3(0, 0, -1); - this.lightCubeDir[5] = new Vec3(0, 0, 1); - this.animTilesParams = new Float32Array(2); this.animParams = new Float32Array(4); this.animIndexParams = new Float32Array(2); diff --git a/src/scene/renderer/forward-renderer.js b/src/scene/renderer/forward-renderer.js index 927b1a98973..709995e9d3d 100644 --- a/src/scene/renderer/forward-renderer.js +++ b/src/scene/renderer/forward-renderer.js @@ -1100,6 +1100,12 @@ class ForwardRenderer extends Renderer { // add debug mesh instances to visible list this.scene.immediate.onPreRenderLayer(layer, visible, transparent); + // set up layer uniforms + if (layer.requiresLightCube) { + this.lightCube.update(this.scene.ambientLight, layer._lights); + this.constantLightCube.setValue(this.lightCube.colors); + } + // upload clustered lights uniforms if (clusteredLightingEnabled && renderAction.lightClusters) { renderAction.lightClusters.activate(); diff --git a/src/scene/renderer/renderer.js b/src/scene/renderer/renderer.js index ccb2dc7711a..f6d26f0eaad 100644 --- a/src/scene/renderer/renderer.js +++ b/src/scene/renderer/renderer.js @@ -13,6 +13,7 @@ import { } from '../constants.js'; import { LightTextureAtlas } from '../lighting/light-texture-atlas.js'; import { Material } from '../materials/material.js'; +import { LightCube } from '../graphics/light-cube.js'; import { CLEARFLAG_COLOR, CLEARFLAG_DEPTH, CLEARFLAG_STENCIL, @@ -169,6 +170,10 @@ class Renderer { this.morphPositionTex = scope.resolve('morphPositionTex'); this.morphNormalTex = scope.resolve('morphNormalTex'); this.morphTexParams = scope.resolve('morph_tex_params'); + + // a single instance of light cube + this.lightCube = new LightCube(); + this.constantLightCube = scope.resolve('lightCube[0]'); } destroy() {