Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactored shadow casting directional lights collection #5593

Merged
merged 3 commits into from
Aug 25, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 0 additions & 5 deletions src/scene/composition/layer-composition.js
Original file line number Diff line number Diff line change
Expand Up @@ -251,9 +251,6 @@ class LayerComposition extends EventHandler {

// if the camera renders any layers.
if (cameraFirstRenderActionIndex < renderActionCount) {
// based on all layers this camera renders, prepare a list of directional lights the camera needs to render shadow for
// and set these up on the first render action for the camera.
this._renderActions[cameraFirstRenderActionIndex].collectDirectionalLights(cameraLayers, this._splitLights[LIGHTTYPE_DIRECTIONAL], this._lights);

// mark the last render action as last one using the camera
lastRenderAction.lastCameraUse = true;
Expand Down Expand Up @@ -313,8 +310,6 @@ class LayerComposition extends EventHandler {
}
}

// split layer lights lists by type
this._splitLightsArray(layer);
layer._dirtyLights = false;
}

Expand Down
31 changes: 1 addition & 30 deletions src/scene/composition/render-action.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,7 @@ class RenderAction {
// true if this is the last render action using this camera
this.lastCameraUse = false;

// directional lights that needs to update their shadows for this render action, stored as a set
this.directionalLightsSet = new Set();

// and also store them as an array
// directional lights that needs to update their shadows for this render action
this.directionalLights = [];

// an array of view bind groups (the number of these corresponds to the number of views when XR is used)
Expand All @@ -70,7 +67,6 @@ class RenderAction {
// prepares render action for re-use
reset() {
this.lightClusters = null;
this.directionalLightsSet.clear();
this.directionalLights.length = 0;
}

Expand All @@ -83,31 +79,6 @@ class RenderAction {
const layer = layerComposition.layerList[this.layerIndex];
return layer.enabled && layerComposition.subLayerEnabled[this.layerIndex];
}

// store directional lights that are needed for this camera based on layers it renders
collectDirectionalLights(cameraLayers, dirLights, allLights) {

this.directionalLightsSet.clear();
this.directionalLights.length = 0;

for (let i = 0; i < dirLights.length; i++) {
const light = dirLights[i];

// only shadow casting lights
if (light.castShadows) {
for (let l = 0; l < cameraLayers.length; l++) {

// if layer has the light
if (cameraLayers[l]._lightsSet.has(light)) {
if (!this.directionalLightsSet.has(light)) {
this.directionalLightsSet.add(light);
this.directionalLights.push(light);
}
}
}
}
}
}
}

export { RenderAction };
59 changes: 49 additions & 10 deletions src/scene/layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { hash32Fnv1a } from '../core/hash.js';

import {
LIGHTTYPE_DIRECTIONAL,
LIGHTTYPE_DIRECTIONAL, LIGHTTYPE_OMNI, LIGHTTYPE_SPOT,

Check failure on line 5 in src/scene/layer.js

View workflow job for this annotation

GitHub Actions / Lint

'LIGHTTYPE_OMNI' is defined but never used

Check failure on line 5 in src/scene/layer.js

View workflow job for this annotation

GitHub Actions / Lint

'LIGHTTYPE_SPOT' is defined but never used
LAYER_FX,
SHADER_FORWARD,
SORTKEY_FORWARD,
Expand Down Expand Up @@ -129,8 +129,26 @@
_clusteredLightsSet = new Set();

/**
* True if the objects rendered on the layer require light cube (emitters with lighting do).
* Lights separated by light type. Lights in the individual arrays are sorted by the key,
* to match their order in _lightIdHash, so that their order matches the order expected by the
* generated shader code.
*
* @type {import('./light.js').Light[][]}
* @private
*/
_splitLights = [[], [], []];

/**
* True if _splitLights needs to be updated, which means if lights were added or removed from
* the layer, or their key changed.
*
* @type {boolean}
* @private
*/
_splitLightsDirty = true;

/**
* True if the objects rendered on the layer require light cube (emitters with lighting do).
*/
requiresLightCube = false;

Expand Down Expand Up @@ -375,21 +393,15 @@
*/
this.customCalculateSortValues = null;

/**
* Lights separated by light type.
*
* @type {import('./light.js').Light[][]}
* @ignore
*/
this._splitLights = [[], [], []];

/**
* @type {import('../framework/components/camera/component.js').CameraComponent[]}
* @ignore
*/
this.cameras = [];

// TODO: remove this when composition no longer updates lights
this._dirtyLights = false;

this._dirtyCameras = false;

// light hash based on the light keys
Expand Down Expand Up @@ -675,6 +687,7 @@
this._dirtyLights = true;
this._lightHashDirty = true;
this._lightIdHashDirty = true;
this._splitLightsDirty = true;
}

/**
Expand Down Expand Up @@ -734,6 +747,32 @@
this.markLightsDirty();
}

get splitLights() {

if (this._splitLightsDirty) {
this._splitLightsDirty = false;

const splitLights = this._splitLights;
for (let i = 0; i < splitLights.length; i++)
splitLights[i].length = 0;

const lights = this._lights;
for (let i = 0; i < lights.length; i++) {
const light = lights[i];
if (light.enabled) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is splitLights updated when lights are enabled and disabled?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, as light is removed from the layer at that point which sets markLightsDirty to true

splitLights[light._type].push(light);
}
}

// sort the lights by their key, as the order of lights is used to generate shader generation key,
// and this avoids new shaders being generated when lights are reordered
for (let i = 0; i < splitLights.length; i++)
splitLights[i].sort((a, b) => a.key - b.key);
}

return this._splitLights;
}

evaluateLightHash(localLights, directionalLights, useIds) {

let hash = 0;
Expand Down
1 change: 1 addition & 0 deletions src/scene/light.js
Original file line number Diff line number Diff line change
Expand Up @@ -889,6 +889,7 @@ class Light {
layersDirty() {
this.layers.forEach((layer) => {
layer._dirtyLights = true;
layer._splitLightsDirty = true;
});
}

Expand Down
4 changes: 2 additions & 2 deletions src/scene/renderer/forward-renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -848,7 +848,7 @@ class ForwardRenderer extends Renderer {

// directional shadows get re-rendered for each camera
if (renderAction.hasDirectionalShadowLights && camera) {
this._shadowRendererDirectional.buildFrameGraph(frameGraph, renderAction, camera);
this._shadowRendererDirectional.buildFrameGraph(frameGraph, renderAction.directionalLights, camera);
}

// start of block of render actions rendering to the same render target
Expand Down Expand Up @@ -1125,7 +1125,7 @@ class ForwardRenderer extends Renderer {
const draws = this._forwardDrawCalls;
this.renderForward(camera.camera,
visible,
layer._splitLights,
layer.splitLights,
shaderPass,
layer.onDrawCall,
layer,
Expand Down
33 changes: 27 additions & 6 deletions src/scene/renderer/renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -938,15 +938,36 @@ class Renderer {
}
}

// shadow casters culling for global (directional) lights
// render actions store which directional lights are needed for each camera, so these are getting culled
// shadow casters culling for directional lights
const renderActions = comp._renderActions;
for (let i = 0; i < renderActions.length; i++) {
const renderAction = renderActions[i];
const count = renderAction.directionalLights.length;
for (let j = 0; j < count; j++) {
const light = renderAction.directionalLights[j];
this._shadowRendererDirectional.cull(light, comp, renderAction.camera.camera);
const camera = renderAction.camera.camera;

// first use of each camera renders directional shadows
if (renderAction.firstCameraUse) {

// get directional lights from all layers of the camera
const cameraLayers = camera.layers;
for (let l = 0; l < cameraLayers.length; l++) {
const cameraLayer = comp.getLayerById(cameraLayers[l]);
const layerDirLights = cameraLayer.splitLights[LIGHTTYPE_DIRECTIONAL];

for (let j = 0; j < layerDirLights.length; j++) {
const light = layerDirLights[j];

// unique shadow casting lights
if (light.castShadows && !_tempSet.has(light)) {
_tempSet.add(light);
renderAction.directionalLights.push(light);

// frustum culling for the directional shadow when rendering the camera
this._shadowRendererDirectional.cull(light, comp, camera);
}
}
}

_tempSet.clear();
}
}
}
Expand Down
11 changes: 5 additions & 6 deletions src/scene/renderer/shadow-renderer-directional.js
Original file line number Diff line number Diff line change
Expand Up @@ -247,16 +247,15 @@ class ShadowRendererDirectional {
* Builds a frame graph for rendering of directional shadows for the render action.
*
* @param {import('../frame-graph.js').FrameGraph} frameGraph - The frame-graph that is built.
* @param {import('../composition/render-action.js').RenderAction} renderAction - The render
* action.
* @param {import('../light.js').Light[]} directionalLights - The
* directional lights.
* @param {import('../../framework/components/camera/component.js').CameraComponent} camera - The camera.
*/
buildFrameGraph(frameGraph, renderAction, camera) {
buildFrameGraph(frameGraph, directionalLights, camera) {

// create required render passes per light
const lights = renderAction.directionalLights;
for (let i = 0; i < lights.length; i++) {
const light = lights[i];
for (let i = 0; i < directionalLights.length; i++) {
const light = directionalLights[i];
Debug.assert(light && light._type === LIGHTTYPE_DIRECTIONAL);

if (this.shadowRenderer.needsShadowRendering(light)) {
Expand Down