UNPKG

playcanvas

Version:

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

151 lines (150 loc) 5.68 kB
var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); import { Debug } from "../../core/debug.js"; import { Vec4 } from "../../core/math/vec4.js"; import { Mat4 } from "../../core/math/mat4.js"; import { SEMANTIC_POSITION } from "../../platform/graphics/constants.js"; import { DebugGraphics } from "../../platform/graphics/debug-graphics.js"; import { LIGHTTYPE_DIRECTIONAL, LIGHTTYPE_OMNI } from "../constants.js"; import { ShaderUtils } from "../shader-lib/shader-utils.js"; import { LightCamera } from "./light-camera.js"; import { QuadRender } from "../graphics/quad-render.js"; import { RenderPass } from "../../platform/graphics/render-pass.js"; const _viewport = new Vec4(); const _invViewProjMatrices = []; class RenderPassCookieRenderer extends RenderPass { constructor(device, cubeSlotsOffsets) { super(device); /** @type {QuadRender|null} */ __publicField(this, "_quadRenderer2D", null); /** @type {QuadRender|null} */ __publicField(this, "_quadRendererCube", null); __publicField(this, "_filteredLights", []); __publicField(this, "_forceCopy", false); /** * Event handle for device restored event. * * @type {EventHandle|null} * @private */ __publicField(this, "_evtDeviceRestored", null); this._cubeSlotsOffsets = cubeSlotsOffsets; this.requiresCubemaps = false; this.blitTextureId = device.scope.resolve("blitTexture"); this.invViewProjId = device.scope.resolve("invViewProj"); this._evtDeviceRestored = device.on("devicerestored", this.onDeviceRestored, this); } destroy() { this._quadRenderer2D?.destroy(); this._quadRenderer2D = null; this._quadRendererCube?.destroy(); this._quadRendererCube = null; this._evtDeviceRestored?.off(); this._evtDeviceRestored = null; } static create(renderTarget, cubeSlotsOffsets) { Debug.assert(renderTarget); const renderPass = new RenderPassCookieRenderer(renderTarget.device, cubeSlotsOffsets); renderPass.init(renderTarget); renderPass.colorOps.clear = false; renderPass.depthStencilOps.clearDepth = false; return renderPass; } onDeviceRestored() { this._forceCopy = true; } update(lights) { const filteredLights = this._filteredLights; this.filter(lights, filteredLights); this.executeEnabled = filteredLights.length > 0; } filter(lights, filteredLights) { for (let i = 0; i < lights.length; i++) { const light = lights[i]; if (light._type === LIGHTTYPE_DIRECTIONAL) { continue; } if (!light.atlasViewportAllocated) { continue; } if (!light.atlasSlotUpdated && !this._forceCopy) { continue; } if (light.enabled && light.cookie && light.visibleThisFrame) { filteredLights.push(light); } } this._forceCopy = false; } initInvViewProjMatrices() { if (!_invViewProjMatrices.length) { for (let face = 0; face < 6; face++) { const camera = LightCamera.create(this.device, null, LIGHTTYPE_OMNI, face); const projMat = camera.projectionMatrix; const viewMat = camera.node.getLocalTransform().clone().invert(); _invViewProjMatrices[face] = new Mat4().mul2(projMat, viewMat).invert(); } } } get quadRenderer2D() { if (!this._quadRenderer2D) { const shader = ShaderUtils.createShader(this.device, { uniqueName: "cookieRenderer2d", attributes: { vertex_position: SEMANTIC_POSITION }, vertexChunk: "cookieBlitVS", fragmentChunk: "cookieBlit2DPS" }); this._quadRenderer2D = new QuadRender(shader); } return this._quadRenderer2D; } get quadRendererCube() { if (!this._quadRendererCube) { const shader = ShaderUtils.createShader(this.device, { uniqueName: "cookieRendererCube", attributes: { vertex_position: SEMANTIC_POSITION }, vertexChunk: "cookieBlitVS", fragmentChunk: "cookieBlitCubePS" }); this._quadRendererCube = new QuadRender(shader); } return this._quadRendererCube; } execute() { const device = this.device; device.setDrawStates(); const renderTargetWidth = this.renderTarget.colorBuffer.width; const cubeSlotsOffsets = this._cubeSlotsOffsets; const filteredLights = this._filteredLights; for (let i = 0; i < filteredLights.length; i++) { const light = filteredLights[i]; DebugGraphics.pushGpuMarker(this.device, `COOKIE ${light._node.name}`); const faceCount = light.numShadowFaces; const quad = faceCount > 1 ? this.quadRendererCube : this.quadRenderer2D; if (faceCount > 1) { this.initInvViewProjMatrices(); } this.blitTextureId.setValue(light.cookie); for (let face = 0; face < faceCount; face++) { _viewport.copy(light.atlasViewport); if (faceCount > 1) { const smallSize = _viewport.z / 3; const offset = cubeSlotsOffsets[face]; _viewport.x += smallSize * offset.x; _viewport.y += smallSize * offset.y; _viewport.z = smallSize; _viewport.w = smallSize; this.invViewProjId.setValue(_invViewProjMatrices[face].data); } _viewport.mulScalar(renderTargetWidth); quad.render(_viewport); } DebugGraphics.popGpuMarker(device); } filteredLights.length = 0; } } export { RenderPassCookieRenderer };