Skip to content

Commit

Permalink
Directional shadows are rendered using RenderPass setup (#4917)
Browse files Browse the repository at this point in the history
Co-authored-by: Martin Valigursky <mvaligursky@snapchat.com>
  • Loading branch information
mvaligursky and Martin Valigursky committed Dec 9, 2022
1 parent aeae825 commit b07a438
Show file tree
Hide file tree
Showing 7 changed files with 194 additions and 105 deletions.
2 changes: 1 addition & 1 deletion src/framework/lightmapper/lightmapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -853,7 +853,7 @@ class Lightmapper {

if (light.type === LIGHTTYPE_DIRECTIONAL) {
this.renderer._shadowRendererDirectional.cull(light, casters, this.camera);
this.renderer.renderShadowsDirectional(lightArray[light.type], this.camera);
this.renderer._shadowRendererDirectional.render(light, this.camera);
} else {
this.renderer._shadowRendererLocal.cull(light, casters);
this.renderer.renderShadowsLocal(lightArray[light.type], this.camera);
Expand Down
8 changes: 7 additions & 1 deletion src/platform/graphics/render-pass.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,16 @@ class RenderPass {
* graphics device.
* @param {Function} execute - Custom function that is called when the pass needs to be
* rendered.
* @param {Function} [after] - Custom function that is called after the pass has fnished.
*/
constructor(graphicsDevice, execute) {
constructor(graphicsDevice, execute, after = null) {
this.device = graphicsDevice;

/** @type {Function} */
this.execute = execute;

/** @type {Function} */
this.after = after;
}

/**
Expand Down Expand Up @@ -217,6 +221,8 @@ class RenderPass {
device.endPass(this);
}

this.after?.();

DebugGraphics.popGpuMarker(device);

}
Expand Down
28 changes: 18 additions & 10 deletions src/scene/frame-graph.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,17 +131,20 @@ class FrameGraph {

this.renderPasses.forEach((renderPass, index) => {

const hasColor = renderPass.renderTarget?.colorBuffer;
const hasDepth = renderPass.renderTarget?.depth;
const hasStencil = renderPass.renderTarget?.stencil;
const rt = renderPass.renderTarget === undefined ? '' : ` RT: ${(renderPass.renderTarget ? renderPass.renderTarget.name : 'NULL')} ` +
`${renderPass.renderTarget?.colorBuffer ? '[Color]' : ''}` +
`${renderPass.renderTarget?.depth ? '[Depth]' : ''}` +
`${renderPass.renderTarget?.stencil ? '[Stencil]' : ''}` +
`${hasColor ? '[Color]' : ''}` +
`${hasDepth ? '[Depth]' : ''}` +
`${hasStencil ? '[Stencil]' : ''}` +
`${(renderPass.samples > 0 ? ' samples: ' + renderPass.samples : '')}`;

Debug.trace(TRACEID_RENDER_PASS,
`${index.toString().padEnd(2, ' ')}: ${renderPass.name.padEnd(20, ' ')}` +
rt.padEnd(30));

if (renderPass.colorOps) {
if (renderPass.colorOps && hasColor) {
Debug.trace(TRACEID_RENDER_PASS_DETAIL, ` colorOps: ` +
`${renderPass.colorOps.clear ? 'clear' : 'load'}->` +
`${renderPass.colorOps.store ? 'store' : 'discard'} ` +
Expand All @@ -150,13 +153,18 @@ class FrameGraph {
}

if (renderPass.depthStencilOps) {
Debug.trace(TRACEID_RENDER_PASS_DETAIL, ` depthOps: ` +
`${renderPass.depthStencilOps.clearDepth ? 'clear' : 'load'}->` +
`${renderPass.depthStencilOps.storeDepth ? 'store' : 'discard'}`);

Debug.trace(TRACEID_RENDER_PASS_DETAIL, ` stencOps: ` +
`${renderPass.depthStencilOps.clearStencil ? 'clear' : 'load'}->` +
`${renderPass.depthStencilOps.storeStencil ? 'store' : 'discard'}`);
if (hasDepth) {
Debug.trace(TRACEID_RENDER_PASS_DETAIL, ` depthOps: ` +
`${renderPass.depthStencilOps.clearDepth ? 'clear' : 'load'}->` +
`${renderPass.depthStencilOps.storeDepth ? 'store' : 'discard'}`);
}

if (hasStencil) {
Debug.trace(TRACEID_RENDER_PASS_DETAIL, ` stencOps: ` +
`${renderPass.depthStencilOps.clearStencil ? 'clear' : 'load'}->` +
`${renderPass.depthStencilOps.storeStencil ? 'store' : 'discard'}`);
}
}
});
}
Expand Down
52 changes: 1 addition & 51 deletions src/scene/renderer/forward-renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ class ForwardRenderer extends Renderer {

this._forwardDrawCalls = 0;
this._materialSwitches = 0;
this._shadowMapTime = 0;
this._depthMapTime = 0;
this._forwardTime = 0;
this._sortTime = 0;
Expand Down Expand Up @@ -422,10 +421,6 @@ class ForwardRenderer extends Renderer {

const isClustered = this.scene.clusteredLightingEnabled;

// #if _PROFILER
const shadowMapStartTime = now();
// #endif

for (let i = 0; i < lights.length; i++) {
const light = lights[i];
Debug.assert(light._type !== LIGHTTYPE_DIRECTIONAL);
Expand All @@ -445,28 +440,6 @@ class ForwardRenderer extends Renderer {

this._shadowRendererLocal.render(light, camera);
}

// #if _PROFILER
this._shadowMapTime += now() - shadowMapStartTime;
// #endif
}

renderShadowsDirectional(lights, camera) {

// #if _PROFILER
const shadowMapStartTime = now();
// #endif

for (let i = 0; i < lights.length; i++) {
const light = lights[i];
Debug.assert(light._type === LIGHTTYPE_DIRECTIONAL);

this._shadowRendererDirectional.render(light, camera);
}

// #if _PROFILER
this._shadowMapTime += now() - shadowMapStartTime;
// #endif
}

// execute first pass over draw calls, in order to update materials / shaders
Expand Down Expand Up @@ -921,12 +894,7 @@ class ForwardRenderer extends Renderer {

// directional shadows get re-rendered for each camera
if (renderAction.hasDirectionalShadowLights && camera) {
const renderPass = new RenderPass(this.device, () => {
this.renderPassDirectionalShadows(renderAction, layerComposition);
});
renderPass.requiresCubemaps = false;
DebugHelper.setName(renderPass, `DirShadowMap`);
frameGraph.addRenderPass(renderPass);
this._shadowRendererDirectional.buildFrameGraph(frameGraph, renderAction, camera);
}

// start of block of render actions rendering to the same render target
Expand Down Expand Up @@ -1052,24 +1020,6 @@ class ForwardRenderer extends Renderer {
this.gpuUpdate(comp._meshInstances);
}

/**
* Render pass for directional shadow maps of the camera.
*
* @param {import('../composition/render-action.js').RenderAction} renderAction - The render
* action.
* @param {import('../composition/layer-composition.js').LayerComposition} layerComposition - The
* layer composition.
* @ignore
*/
renderPassDirectionalShadows(renderAction, layerComposition) {

Debug.assert(renderAction.directionalLights.length > 0);
const layer = layerComposition.layerList[renderAction.layerIndex];
const camera = layer.cameras[renderAction.cameraIndex];

this.renderShadowsDirectional(renderAction.directionalLights, camera.camera);
}

renderPassPostprocessing(renderAction, layerComposition) {

const layer = layerComposition.layerList[renderAction.layerIndex];
Expand Down
3 changes: 2 additions & 1 deletion src/scene/renderer/renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ class Renderer {
this._skinTime = 0;
this._morphTime = 0;
this._cullTime = 0;
this._shadowMapTime = 0;
this._lightClustersTime = 0;
this._layerCompositionUpdateTime = 0;

Expand Down Expand Up @@ -384,7 +385,7 @@ class Renderer {

// make sure colorWrite is set to true to all channels, if you want to fully clear the target
// TODO: this function is only used from outside of forward renderer, and should be deprecated
// when the functionality moves to the render passes.
// when the functionality moves to the render passes. Note that Editor uses it as well.
setCamera(camera, target, clear, renderAction = null) {

this.setCameraUniforms(camera, target, renderAction);
Expand Down
60 changes: 60 additions & 0 deletions src/scene/renderer/shadow-renderer-directional.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { Debug, DebugHelper } from '../../core/debug.js';
import { Vec3 } from '../../core/math/vec3.js';
import { Mat4 } from '../../core/math/mat4.js';
import { BoundingBox } from '../../core/shape/bounding-box.js';

import {
LIGHTTYPE_DIRECTIONAL
} from '../constants.js';
import { RenderPass } from '../../platform/graphics/render-pass.js';

import { ShadowRenderer } from "./shadow-renderer.js";
import { ShadowMap } from './shadow-map.js';

Expand Down Expand Up @@ -153,6 +159,60 @@ class ShadowRendererDirectional extends ShadowRenderer {
shadowCam.farClip = depthRange.max - depthRange.min + 0.2;
}
}

addLightRenderPasses(frameGraph, light, camera) {

// shadow cascades have more faces rendered within a singe render pass
const faceCount = light.numShadowFaces;

// prepare render targets / cameras for rendering
let shadowCamera;
for (let face = 0; face < faceCount; face++) {
shadowCamera = this.prepareFace(light, camera, face);
}

const renderPass = new RenderPass(this.device, () => {

// inside the render pass, render all faces
for (let face = 0; face < faceCount; face++) {
this.renderFace(light, camera, face, false);
}

}, () => {

// after the pass is done, apply VSM blur if needed
this.renderVms(light, camera);

});

// setup render pass using any of the cameras, they all have the same pass related properties
this.setupRenderPass(renderPass, shadowCamera);
DebugHelper.setName(renderPass, `DirShadow-${light._node.name}`);

frameGraph.addRenderPass(renderPass);
}

/**
* 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('../../framework/components/camera/component.js').CameraComponent} camera - The camera.
*/
buildFrameGraph(frameGraph, renderAction, camera) {

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

if (this.needsShadowRendering(light)) {
this.addLightRenderPasses(frameGraph, light, camera.camera);
}
}
}
}

export { ShadowRendererDirectional };

0 comments on commit b07a438

Please sign in to comment.