UNPKG

@babylonjs/core

Version:

Getting started? Play directly with the Babylon.js API using our [playground](https://playground.babylonjs.com/). It also contains a lot of samples to learn how to use it.

218 lines 9.04 kB
import { Matrix, Quaternion, Vector3 } from "../../../../Maths/math.vector.js"; import { Observable } from "../../../../Misc/observable.js"; import { _IblShadowsVoxelRenderer } from "../../../../Rendering/IBLShadows/iblShadowsVoxelRenderer.js"; import { FrameGraphTask } from "../../../frameGraphTask.js"; /** * Task used to voxelize shadow casting objects for IBL shadows. * @internal */ export class FrameGraphIblShadowsVoxelizationTask extends FrameGraphTask { /** * Sets voxel grid resolution exponent. Actual resolution is 2^resolutionExp. */ set resolutionExp(value) { const newValue = Math.round(Math.max(1, Math.min(value, 8))); if (newValue === this._resolutionExp) { return; } this._resolutionExp = newValue; if (this._voxelRenderer) { this._voxelRenderer.voxelResolutionExp = this._resolutionExp; } this.dirty = true; } /** * Gets voxel grid resolution exponent. Actual resolution is 2^resolutionExp. */ get resolutionExp() { return this._resolutionExp; } /** * Controls how often voxelization is refreshed. * - -1: manual only (requires setting `dirty = true`) * - 0: every frame * - 1: skip 1 frame between updates * - N: skip N frames between updates */ get refreshRate() { return this._refreshRate; } set refreshRate(value) { this._refreshRate = Math.max(-1, Math.round(value)); } /** * Creates a new voxelization task. * @param name The task name. * @param frameGraph The frame graph this task belongs to. */ constructor(name, frameGraph) { super(name, frameGraph); /** * Observable raised when voxelization completes. */ this.onVoxelizationCompleteObservable = new Observable(); /** * World-space voxel grid size. */ this.voxelGridSize = 1; /** * Voxel grid resolution exponent. Actual resolution is 2^resolutionExp. */ this._resolutionExp = 6; /** * Enables tri-planar voxelization mode. */ this.triPlanarVoxelization = true; /** * Indicates whether voxelization should be refreshed. */ this.dirty = true; this._refreshRate = -1; /** * World-to-voxel normalization matrix used by tracing. */ this.worldScaleMatrix = Matrix.Identity(); this._voxelizationCompleteObserver = null; this._frameCounter = 0; this.outputVoxelGridTexture = this._frameGraph.textureManager.createDanglingHandle(); } /** * Gets the class name. * @returns The class name. */ getClassName() { return "FrameGraphIblShadowsVoxelizationTask"; } /** * Checks whether the task has all required inputs. * @returns True when ready. */ isReady() { return true; } /** * Requests a voxelization update on the next eligible frame. */ requestVoxelizationUpdate() { this.dirty = true; } /** * Recomputes voxel world bounds from the current object list and updates worldScaleMatrix. */ updateSceneBounds() { this._updateWorldScaleMatrix(); } /** * Records the voxelization passes. */ record() { if (this.objectList === undefined) { throw new Error(`FrameGraphIblShadowsVoxelizationTask ${this.name}: objectList is required`); } this._ensureVoxelRenderer(); this._updateWorldScaleMatrix(); this._updateOutputTextureHandlesFromRenderer(); const voxelRT = this._voxelRenderer.getRT(); const voxelRTInternalTexture = voxelRT.getInternalTexture(); if (!voxelRTInternalTexture) { throw new Error(`FrameGraphIblShadowsVoxelizationTask ${this.name}: voxel renderer RT texture is unavailable`); } this._voxelRTTextureHandle = this._frameGraph.textureManager.importTexture(`${this.name} Voxel RT`, voxelRTInternalTexture, this._voxelRTTextureHandle); const pass = this._frameGraph.addRenderPass(this.name); pass.setRenderTarget(this._voxelRTTextureHandle); pass.addDependencies(this.outputVoxelGridTexture); pass.setExecuteFunc((context) => { context.restoreDefaultFramebuffer(); this._frameCounter++; const shouldRefreshFromRate = this.refreshRate >= 0 && (this._frameCounter - 1) % (this.refreshRate + 1) === 0; if (this._voxelRenderer.isVoxelizationInProgress()) { this._voxelRenderer.processVoxelization(); return; } if (this.dirty || shouldRefreshFromRate) { const meshes = this.objectList.meshes; if (meshes.length === 0) { return; } this._ensureVoxelRenderer(); this._updateWorldScaleMatrix(); this._voxelRenderer.setWorldScaleMatrix(this.worldScaleMatrix); this._voxelRenderer.updateVoxelGrid(meshes, false); this.dirty = false; } if (this._voxelRenderer.isVoxelizationInProgress()) { this._voxelRenderer.processVoxelization(); } }); } /** * Disposes internal resources. */ dispose() { this._detachVoxelizationObserver(); this._voxelRenderer?.dispose(); this._voxelRenderer = undefined; this.onVoxelizationCompleteObservable.clear(); super.dispose(); } _ensureVoxelRenderer() { const needsNewRenderer = !this._voxelRenderer || this._voxelRendererResolutionExp !== this.resolutionExp || this._voxelRendererTriPlanar !== this.triPlanarVoxelization; if (!needsNewRenderer) { return; } this._voxelRenderer?.dispose(); this._voxelRenderer = new _IblShadowsVoxelRenderer(this._frameGraph.scene, {}, this.resolutionExp, this.triPlanarVoxelization); this._attachVoxelizationObserver(); this._voxelRendererResolutionExp = this.resolutionExp; this._voxelRendererTriPlanar = this.triPlanarVoxelization; } _attachVoxelizationObserver() { this._detachVoxelizationObserver(); if (!this._voxelRenderer) { return; } this._voxelizationCompleteObserver = this._voxelRenderer.onVoxelizationCompleteObservable.add(() => { this._updateOutputTextureHandlesFromRenderer(); this.onVoxelizationCompleteObservable.notifyObservers(); }); } _detachVoxelizationObserver() { if (this._voxelRenderer && this._voxelizationCompleteObserver) { this._voxelRenderer.onVoxelizationCompleteObservable.remove(this._voxelizationCompleteObserver); } this._voxelizationCompleteObserver = null; } _updateWorldScaleMatrix() { const meshes = this.objectList?.meshes; if (!meshes || meshes.length === 0 || !isFinite(this.voxelGridSize) || this.voxelGridSize <= 0) { this.worldScaleMatrix.copyFrom(Matrix.Identity()); return; } const bounds = { min: new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE), max: new Vector3(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE), }; for (const mesh of meshes) { const localBounds = mesh.getHierarchyBoundingVectors(true); bounds.min = Vector3.Minimize(bounds.min, localBounds.min); bounds.max = Vector3.Maximize(bounds.max, localBounds.max); } const size = bounds.max.subtract(bounds.min); this.voxelGridSize = Math.max(size.x, size.y, size.z); const halfSize = this.voxelGridSize / 2.0; const centerOffset = bounds.max.add(bounds.min).multiplyByFloats(-0.5, -0.5, -0.5); const invWorldScaleMatrix = Matrix.Compose(new Vector3(1.0 / halfSize, 1.0 / halfSize, 1.0 / halfSize), new Quaternion(), Vector3.Zero()); const invTranslationMatrix = Matrix.Compose(new Vector3(1.0, 1.0, 1.0), new Quaternion(), centerOffset); invTranslationMatrix.multiplyToRef(invWorldScaleMatrix, this.worldScaleMatrix); } _updateOutputTextureHandlesFromRenderer() { const voxelTexture = this._voxelRenderer.getVoxelGrid(); const voxelInternalTexture = voxelTexture.getInternalTexture(); if (!voxelInternalTexture) { throw new Error(`FrameGraphIblShadowsVoxelizationTask ${this.name}: voxel renderer texture is unavailable`); } this._voxelGridTextureHandle = this._frameGraph.textureManager.importTexture(`${this.name} Voxel Grid`, voxelInternalTexture, this._voxelGridTextureHandle); this._frameGraph.textureManager.resolveDanglingHandle(this.outputVoxelGridTexture, this._voxelGridTextureHandle); } } //# sourceMappingURL=iblShadowsVoxelizationTask.js.map