Skip to content

Commit

Permalink
Refactored and fixed the lightcube lighting for particle emitters (#5570
Browse files Browse the repository at this point in the history
)

* Refactored and fixed the lightcube lighting for particle emitters

* lint imports

* small improvement

---------

Co-authored-by: Martin Valigursky <mvaligursky@snapchat.com>
  • Loading branch information
mvaligursky and Martin Valigursky authored Aug 17, 2023
1 parent 1cde179 commit e4731b0
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 44 deletions.
38 changes: 10 additions & 28 deletions src/framework/components/particle-system/system.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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)) {
Expand All @@ -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) {
Expand Down
49 changes: 49 additions & 0 deletions src/scene/graphics/light-cube.js
Original file line number Diff line number Diff line change
@@ -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 };
11 changes: 5 additions & 6 deletions src/scene/layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down Expand Up @@ -412,12 +417,6 @@ class Layer {
// #endif

this._shaderVersion = -1;

/**
* @type {Float32Array|null}
* @ignore
*/
this._lightCube = null;
}

/**
Expand Down
10 changes: 0 additions & 10 deletions src/scene/particle-system/particle-emitter.js
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
Expand All @@ -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);
Expand Down
6 changes: 6 additions & 0 deletions src/scene/renderer/forward-renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
5 changes: 5 additions & 0 deletions src/scene/renderer/renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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() {
Expand Down

0 comments on commit e4731b0

Please sign in to comment.