UNPKG

playcanvas

Version:

Open-source WebGL/WebGPU 3D engine for the web

185 lines (184 loc) 6.52 kB
import { TRACEID_RENDER_PASS_DETAIL } from "../../core/constants.js"; import { now } from "../../core/time.js"; import { Tracing } from "../../core/tracing.js"; import { BlendState } from "../../platform/graphics/blend-state.js"; import { RenderPass } from "../../platform/graphics/render-pass.js"; import { RenderAction } from "../composition/render-action.js"; import { EVENT_POSTRENDER, EVENT_POSTRENDER_LAYER, EVENT_PRERENDER, EVENT_PRERENDER_LAYER, SHADER_FORWARD } from "../constants.js"; class RenderPassForward extends RenderPass { layerComposition; scene; renderer; renderActions = []; gammaCorrection; toneMapping; noDepthClear = false; constructor(device, layerComposition, scene, renderer) { super(device); this.layerComposition = layerComposition; this.scene = scene; this.renderer = renderer; } get rendersAnything() { return this.renderActions.length > 0; } addRenderAction(renderAction) { this.renderActions.push(renderAction); } addLayer(cameraComponent, layer, transparent, autoClears = true) { const ra = new RenderAction(); ra.renderTarget = this.renderTarget; ra.camera = cameraComponent; ra.layer = layer; ra.transparent = transparent; if (autoClears) { const firstRa = this.renderActions.length === 0; ra.setupClears(firstRa ? cameraComponent : void 0, layer); } this.addRenderAction(ra); } addLayers(composition, cameraComponent, startIndex, firstLayerClears, lastLayerId, lastLayerIsTransparent = true) { const { layerList, subLayerList } = composition; let clearRenderTarget = firstLayerClears; let index = startIndex; while (index < layerList.length) { const layer = layerList[index]; const isTransparent = subLayerList[index]; const renderedByCamera = cameraComponent.camera.layersSet.has(layer.id); if (renderedByCamera) { this.addLayer(cameraComponent, layer, isTransparent, clearRenderTarget); clearRenderTarget = false; } index++; if (layer.id === lastLayerId && isTransparent === lastLayerIsTransparent) { break; } } return index; } // Collect before-passes from cameras whose first render action lives in this // RenderPassForward. Uses the existing firstCameraUse flag (set by LayerComposition) // to guarantee each camera's before-passes are scheduled exactly once, even when // multiple RenderPassForward instances reference the same camera (e.g. CameraFrame's // scenePass vs afterPass). updateCameraBeforePasses() { for (let i = 0; i < this.renderActions.length; i++) { const ra = this.renderActions[i]; if (ra.firstCameraUse) { const camera = ra.camera?.camera; if (camera) { const { beforePasses } = camera; for (let j = 0; j < beforePasses.length; j++) { this.beforePasses.push(beforePasses[j]); } } } } } updateDirectionalShadows() { const { renderer, renderActions } = this; for (let i = 0; i < renderActions.length; i++) { const renderAction = renderActions[i]; const cameraComp = renderAction.camera; const camera = cameraComp.camera; const shadowDirLights = this.renderer.cameraDirShadowLights.get(camera); if (shadowDirLights) { for (let l = 0; l < shadowDirLights.length; l++) { const light = shadowDirLights[l]; if (renderer.dirLightShadows.get(light) !== camera) { renderer.dirLightShadows.set(light, camera); const shadowPass = renderer._shadowRendererDirectional.getLightRenderPass(light, camera); if (shadowPass) { this.beforePasses.push(shadowPass); } } } } } } updateClears() { const renderAction = this.renderActions[0]; if (renderAction) { const cameraComponent = renderAction.camera; const camera = cameraComponent.camera; const fullSizeClearRect = camera.fullSizeClearRect; this.setClearColor(fullSizeClearRect && renderAction.clearColor ? camera.clearColor : void 0); this.setClearDepth(fullSizeClearRect && renderAction.clearDepth && !this.noDepthClear ? camera.clearDepth : void 0); this.setClearStencil(fullSizeClearRect && renderAction.clearStencil ? camera.clearStencil : void 0); } } frameUpdate() { super.frameUpdate(); this.updateCameraBeforePasses(); this.updateDirectionalShadows(); this.updateClears(); } before() { const { renderActions } = this; for (let i = 0; i < renderActions.length; i++) { const ra = renderActions[i]; if (ra.firstCameraUse) { this.scene.fire(EVENT_PRERENDER, ra.camera); } } } execute() { const { layerComposition, renderActions } = this; for (let i = 0; i < renderActions.length; i++) { const ra = renderActions[i]; const layer = ra.layer; if (layerComposition.isEnabled(layer, ra.transparent)) { this.renderRenderAction(ra, i === 0); } } } after() { for (let i = 0; i < this.renderActions.length; i++) { const ra = this.renderActions[i]; if (ra.lastCameraUse) { this.scene.fire(EVENT_POSTRENDER, ra.camera); } } this.beforePasses.length = 0; } renderRenderAction(renderAction, firstRenderAction) { const { renderer, scene } = this; const device = renderer.device; const { layer, transparent, camera } = renderAction; if (camera) { const originalGammaCorrection = camera.gammaCorrection; const originalToneMapping = camera.toneMapping; if (this.gammaCorrection !== void 0) camera.gammaCorrection = this.gammaCorrection; if (this.toneMapping !== void 0) camera.toneMapping = this.toneMapping; scene.fire(EVENT_PRERENDER_LAYER, camera, layer, transparent); const options = { lightClusters: renderAction.lightClusters }; const shaderPass = camera.camera.shaderPassInfo?.index ?? SHADER_FORWARD; if (!firstRenderAction || !camera.camera.fullSizeClearRect) { options.clearColor = renderAction.clearColor; options.clearDepth = renderAction.clearDepth; options.clearStencil = renderAction.clearStencil; } const renderTarget = renderAction.renderTarget ?? device.backBuffer; renderer.renderForwardLayer( camera.camera, renderTarget, layer, transparent, shaderPass, renderAction.viewBindGroups, options ); device.setBlendState(BlendState.NOBLEND); device.setStencilState(null, null); device.setAlphaToCoverage(false); scene.fire(EVENT_POSTRENDER_LAYER, camera, layer, transparent); if (this.gammaCorrection !== void 0) camera.gammaCorrection = originalGammaCorrection; if (this.toneMapping !== void 0) camera.toneMapping = originalToneMapping; } } } export { RenderPassForward };