Skip to content

Commit b07a438

Browse files
mvaligurskyMartin Valigursky
and
Martin Valigursky
authored
Directional shadows are rendered using RenderPass setup (#4917)
Co-authored-by: Martin Valigursky <mvaligursky@snapchat.com>
1 parent aeae825 commit b07a438

File tree

7 files changed

+194
-105
lines changed

7 files changed

+194
-105
lines changed

src/framework/lightmapper/lightmapper.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -853,7 +853,7 @@ class Lightmapper {
853853

854854
if (light.type === LIGHTTYPE_DIRECTIONAL) {
855855
this.renderer._shadowRendererDirectional.cull(light, casters, this.camera);
856-
this.renderer.renderShadowsDirectional(lightArray[light.type], this.camera);
856+
this.renderer._shadowRendererDirectional.render(light, this.camera);
857857
} else {
858858
this.renderer._shadowRendererLocal.cull(light, casters);
859859
this.renderer.renderShadowsLocal(lightArray[light.type], this.camera);

src/platform/graphics/render-pass.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -130,12 +130,16 @@ class RenderPass {
130130
* graphics device.
131131
* @param {Function} execute - Custom function that is called when the pass needs to be
132132
* rendered.
133+
* @param {Function} [after] - Custom function that is called after the pass has fnished.
133134
*/
134-
constructor(graphicsDevice, execute) {
135+
constructor(graphicsDevice, execute, after = null) {
135136
this.device = graphicsDevice;
136137

137138
/** @type {Function} */
138139
this.execute = execute;
140+
141+
/** @type {Function} */
142+
this.after = after;
139143
}
140144

141145
/**
@@ -217,6 +221,8 @@ class RenderPass {
217221
device.endPass(this);
218222
}
219223

224+
this.after?.();
225+
220226
DebugGraphics.popGpuMarker(device);
221227

222228
}

src/scene/frame-graph.js

+18-10
Original file line numberDiff line numberDiff line change
@@ -131,17 +131,20 @@ class FrameGraph {
131131

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

134+
const hasColor = renderPass.renderTarget?.colorBuffer;
135+
const hasDepth = renderPass.renderTarget?.depth;
136+
const hasStencil = renderPass.renderTarget?.stencil;
134137
const rt = renderPass.renderTarget === undefined ? '' : ` RT: ${(renderPass.renderTarget ? renderPass.renderTarget.name : 'NULL')} ` +
135-
`${renderPass.renderTarget?.colorBuffer ? '[Color]' : ''}` +
136-
`${renderPass.renderTarget?.depth ? '[Depth]' : ''}` +
137-
`${renderPass.renderTarget?.stencil ? '[Stencil]' : ''}` +
138+
`${hasColor ? '[Color]' : ''}` +
139+
`${hasDepth ? '[Depth]' : ''}` +
140+
`${hasStencil ? '[Stencil]' : ''}` +
138141
`${(renderPass.samples > 0 ? ' samples: ' + renderPass.samples : '')}`;
139142

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

144-
if (renderPass.colorOps) {
147+
if (renderPass.colorOps && hasColor) {
145148
Debug.trace(TRACEID_RENDER_PASS_DETAIL, ` colorOps: ` +
146149
`${renderPass.colorOps.clear ? 'clear' : 'load'}->` +
147150
`${renderPass.colorOps.store ? 'store' : 'discard'} ` +
@@ -150,13 +153,18 @@ class FrameGraph {
150153
}
151154

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

157-
Debug.trace(TRACEID_RENDER_PASS_DETAIL, ` stencOps: ` +
158-
`${renderPass.depthStencilOps.clearStencil ? 'clear' : 'load'}->` +
159-
`${renderPass.depthStencilOps.storeStencil ? 'store' : 'discard'}`);
157+
if (hasDepth) {
158+
Debug.trace(TRACEID_RENDER_PASS_DETAIL, ` depthOps: ` +
159+
`${renderPass.depthStencilOps.clearDepth ? 'clear' : 'load'}->` +
160+
`${renderPass.depthStencilOps.storeDepth ? 'store' : 'discard'}`);
161+
}
162+
163+
if (hasStencil) {
164+
Debug.trace(TRACEID_RENDER_PASS_DETAIL, ` stencOps: ` +
165+
`${renderPass.depthStencilOps.clearStencil ? 'clear' : 'load'}->` +
166+
`${renderPass.depthStencilOps.storeStencil ? 'store' : 'discard'}`);
167+
}
160168
}
161169
});
162170
}

src/scene/renderer/forward-renderer.js

+1-51
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ class ForwardRenderer extends Renderer {
5454

5555
this._forwardDrawCalls = 0;
5656
this._materialSwitches = 0;
57-
this._shadowMapTime = 0;
5857
this._depthMapTime = 0;
5958
this._forwardTime = 0;
6059
this._sortTime = 0;
@@ -422,10 +421,6 @@ class ForwardRenderer extends Renderer {
422421

423422
const isClustered = this.scene.clusteredLightingEnabled;
424423

425-
// #if _PROFILER
426-
const shadowMapStartTime = now();
427-
// #endif
428-
429424
for (let i = 0; i < lights.length; i++) {
430425
const light = lights[i];
431426
Debug.assert(light._type !== LIGHTTYPE_DIRECTIONAL);
@@ -445,28 +440,6 @@ class ForwardRenderer extends Renderer {
445440

446441
this._shadowRendererLocal.render(light, camera);
447442
}
448-
449-
// #if _PROFILER
450-
this._shadowMapTime += now() - shadowMapStartTime;
451-
// #endif
452-
}
453-
454-
renderShadowsDirectional(lights, camera) {
455-
456-
// #if _PROFILER
457-
const shadowMapStartTime = now();
458-
// #endif
459-
460-
for (let i = 0; i < lights.length; i++) {
461-
const light = lights[i];
462-
Debug.assert(light._type === LIGHTTYPE_DIRECTIONAL);
463-
464-
this._shadowRendererDirectional.render(light, camera);
465-
}
466-
467-
// #if _PROFILER
468-
this._shadowMapTime += now() - shadowMapStartTime;
469-
// #endif
470443
}
471444

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

922895
// directional shadows get re-rendered for each camera
923896
if (renderAction.hasDirectionalShadowLights && camera) {
924-
const renderPass = new RenderPass(this.device, () => {
925-
this.renderPassDirectionalShadows(renderAction, layerComposition);
926-
});
927-
renderPass.requiresCubemaps = false;
928-
DebugHelper.setName(renderPass, `DirShadowMap`);
929-
frameGraph.addRenderPass(renderPass);
897+
this._shadowRendererDirectional.buildFrameGraph(frameGraph, renderAction, camera);
930898
}
931899

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

1055-
/**
1056-
* Render pass for directional shadow maps of the camera.
1057-
*
1058-
* @param {import('../composition/render-action.js').RenderAction} renderAction - The render
1059-
* action.
1060-
* @param {import('../composition/layer-composition.js').LayerComposition} layerComposition - The
1061-
* layer composition.
1062-
* @ignore
1063-
*/
1064-
renderPassDirectionalShadows(renderAction, layerComposition) {
1065-
1066-
Debug.assert(renderAction.directionalLights.length > 0);
1067-
const layer = layerComposition.layerList[renderAction.layerIndex];
1068-
const camera = layer.cameras[renderAction.cameraIndex];
1069-
1070-
this.renderShadowsDirectional(renderAction.directionalLights, camera.camera);
1071-
}
1072-
10731023
renderPassPostprocessing(renderAction, layerComposition) {
10741024

10751025
const layer = layerComposition.layerList[renderAction.layerIndex];

src/scene/renderer/renderer.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ class Renderer {
101101
this._skinTime = 0;
102102
this._morphTime = 0;
103103
this._cullTime = 0;
104+
this._shadowMapTime = 0;
104105
this._lightClustersTime = 0;
105106
this._layerCompositionUpdateTime = 0;
106107

@@ -384,7 +385,7 @@ class Renderer {
384385

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

390391
this.setCameraUniforms(camera, target, renderAction);

src/scene/renderer/shadow-renderer-directional.js

+60
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
1+
import { Debug, DebugHelper } from '../../core/debug.js';
12
import { Vec3 } from '../../core/math/vec3.js';
23
import { Mat4 } from '../../core/math/mat4.js';
34
import { BoundingBox } from '../../core/shape/bounding-box.js';
45

6+
import {
7+
LIGHTTYPE_DIRECTIONAL
8+
} from '../constants.js';
9+
import { RenderPass } from '../../platform/graphics/render-pass.js';
10+
511
import { ShadowRenderer } from "./shadow-renderer.js";
612
import { ShadowMap } from './shadow-map.js';
713

@@ -153,6 +159,60 @@ class ShadowRendererDirectional extends ShadowRenderer {
153159
shadowCam.farClip = depthRange.max - depthRange.min + 0.2;
154160
}
155161
}
162+
163+
addLightRenderPasses(frameGraph, light, camera) {
164+
165+
// shadow cascades have more faces rendered within a singe render pass
166+
const faceCount = light.numShadowFaces;
167+
168+
// prepare render targets / cameras for rendering
169+
let shadowCamera;
170+
for (let face = 0; face < faceCount; face++) {
171+
shadowCamera = this.prepareFace(light, camera, face);
172+
}
173+
174+
const renderPass = new RenderPass(this.device, () => {
175+
176+
// inside the render pass, render all faces
177+
for (let face = 0; face < faceCount; face++) {
178+
this.renderFace(light, camera, face, false);
179+
}
180+
181+
}, () => {
182+
183+
// after the pass is done, apply VSM blur if needed
184+
this.renderVms(light, camera);
185+
186+
});
187+
188+
// setup render pass using any of the cameras, they all have the same pass related properties
189+
this.setupRenderPass(renderPass, shadowCamera);
190+
DebugHelper.setName(renderPass, `DirShadow-${light._node.name}`);
191+
192+
frameGraph.addRenderPass(renderPass);
193+
}
194+
195+
/**
196+
* Builds a frame graph for rendering of directional shadows for the render action.
197+
*
198+
* @param {import('../frame-graph.js').FrameGraph} frameGraph - The frame-graph that is built.
199+
* @param {import('../composition/render-action.js').RenderAction} renderAction - The render
200+
* action.
201+
* @param {import('../../framework/components/camera/component.js').CameraComponent} camera - The camera.
202+
*/
203+
buildFrameGraph(frameGraph, renderAction, camera) {
204+
205+
// create required render passes per light
206+
const lights = renderAction.directionalLights;
207+
for (let i = 0; i < lights.length; i++) {
208+
const light = lights[i];
209+
Debug.assert(light && light._type === LIGHTTYPE_DIRECTIONAL);
210+
211+
if (this.needsShadowRendering(light)) {
212+
this.addLightRenderPasses(frameGraph, light, camera.camera);
213+
}
214+
}
215+
}
156216
}
157217

158218
export { ShadowRendererDirectional };

0 commit comments

Comments
 (0)