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.

365 lines (364 loc) 14.2 kB
import { __decorate } from "../../../tslib.es6.js"; /* eslint-disable @typescript-eslint/naming-convention */ import { serialize } from "../../../Misc/decorators.js"; import { SerializationHelper } from "../../../Misc/decorators.serialization.js"; import { PostProcess } from "../../postProcess.js"; import { PostProcessRenderPipeline } from "../postProcessRenderPipeline.js"; import { PostProcessRenderEffect } from "../postProcessRenderEffect.js"; import { RegisterClass } from "../../../Misc/typeStore.js"; import { PassPostProcess } from "../../passPostProcess.js"; import { ThinTAAPostProcess } from "../../thinTAAPostProcess.js"; import { TAAMaterialManager } from "./taaMaterialManager.js"; import { Logger } from "../../../Misc/logger.js"; import "../postProcessRenderPipelineManagerSceneComponent.js"; class TAAEffectConfiguration { constructor() { this.name = "taa"; this.enabled = true; this.texturesRequired = [11]; } } /** * Simple implementation of Temporal Anti-Aliasing (TAA). * This can be used to improve image quality for still pictures (screenshots for e.g.). */ export class TAARenderingPipeline extends PostProcessRenderPipeline { /** * Number of accumulated samples (default: 16) */ set samples(samples) { this._taaThinPostProcess.samples = samples; } get samples() { return this._taaThinPostProcess.samples; } /** * MSAA samples (default: 1) */ set msaaSamples(samples) { if (this._msaaSamples === samples) { return; } this._msaaSamples = samples; if (this._taaPostProcess) { this._taaPostProcess.samples = samples; } } get msaaSamples() { return this._msaaSamples; } /** * The factor used to blend the history frame with current frame (default: 0.05) */ get factor() { return this._taaThinPostProcess.factor; } set factor(value) { this._taaThinPostProcess.factor = value; } /** * Disable TAA on camera move (default: true). * You generally want to keep this enabled, otherwise you will get a ghost effect when the camera moves (but if it's what you want, go for it!) */ get disableOnCameraMove() { return this._taaThinPostProcess.disableOnCameraMove; } set disableOnCameraMove(value) { this._taaThinPostProcess.disableOnCameraMove = value; } /** * Enables reprojecting the history texture with a per-pixel velocity. */ get reprojectHistory() { return this._taaThinPostProcess.reprojectHistory; } set reprojectHistory(reproject) { if (this.reprojectHistory === reproject) { return; } this._updateReprojection(reproject); } /** * Clamps the history pixel to the min and max of the 3x3 pixels surrounding the target pixel. * This can help further reduce ghosting and artifacts. */ get clampHistory() { return this._taaThinPostProcess.clampHistory; } set clampHistory(history) { this._taaThinPostProcess.clampHistory = history; } /** * Gets or sets a boolean indicating if the render pipeline is enabled (default: true). */ get isEnabled() { return this._isEnabled; } set isEnabled(value) { if (this._isEnabled === value) { return; } this._isEnabled = value; if (this._taaMaterialManager) { this._taaMaterialManager.isEnabled = value && this.reprojectHistory; } if (!value) { if (this._cameras !== null) { this._scene.postProcessRenderPipelineManager.detachCamerasFromRenderPipeline(this._name, this._cameras); this._cameras = this._camerasToBeAttached.slice(); } } else if (value) { if (!this._isDirty) { if (this._cameras !== null) { this._taaThinPostProcess._reset(); this._scene.postProcessRenderPipelineManager.attachCamerasToRenderPipeline(this._name, this._cameras); } } else { this._buildPipeline(); } } } /** * Gets active scene */ get scene() { return this._scene; } /** * Returns true if TAA is supported by the running hardware */ get isSupported() { const caps = this._scene.getEngine().getCaps(); return caps.texelFetch; } /** * Constructor of the TAA rendering pipeline * @param name The rendering pipeline name * @param scene The scene linked to this pipeline * @param cameras The array of cameras that the rendering pipeline will be attached to (default: scene.cameras) * @param textureType The type of texture where the scene will be rendered (default: 0) */ constructor(name, scene, cameras, textureType = 0) { const engine = scene.getEngine(); super(engine, name); /** * The TAA PostProcess effect id in the pipeline */ this.TAARenderEffect = "TAARenderEffect"; /** * The pass PostProcess effect id in the pipeline */ this.TAAPassEffect = "TAAPassEffect"; this._msaaSamples = 1; this._isEnabled = true; this._isDirty = false; this._camerasToBeAttached = []; this._pingpong = 0; this._cameras = cameras || scene.cameras; this._cameras = this._cameras.slice(); this._camerasToBeAttached = this._cameras.slice(); this._scene = scene; this._textureType = textureType; this._taaThinPostProcess = new ThinTAAPostProcess("TAA", this._scene.getEngine()); if (this.isSupported) { this._createPingPongTextures(engine.getRenderWidth(), engine.getRenderHeight()); scene.postProcessRenderPipelineManager.addPipeline(this); this._buildPipeline(); } } /** * Get the class name * @returns "TAARenderingPipeline" */ getClassName() { return "TAARenderingPipeline"; } /** * Adds a camera to the pipeline * @param camera the camera to be added */ addCamera(camera) { this._camerasToBeAttached.push(camera); this._buildPipeline(); } /** * Removes a camera from the pipeline * @param camera the camera to remove */ removeCamera(camera) { const index = this._camerasToBeAttached.indexOf(camera); this._camerasToBeAttached.splice(index, 1); this._buildPipeline(); } /** * Removes the internal pipeline assets and detaches the pipeline from the scene cameras */ dispose() { this._disposePostProcesses(); this._scene.postProcessRenderPipelineManager.detachCamerasFromRenderPipeline(this._name, this._cameras); this._scene.postProcessRenderPipelineManager.removePipeline(this._name); this._taaMaterialManager?.dispose(); this._ping.dispose(); this._pong.dispose(); super.dispose(); } _createPingPongTextures(width, height) { const engine = this._scene.getEngine(); this._ping?.dispose(); this._pong?.dispose(); this._ping = engine.createRenderTargetTexture({ width, height }, { generateMipMaps: false, generateDepthBuffer: false, type: 2, samplingMode: 2 }); this._pong = engine.createRenderTargetTexture({ width, height }, { generateMipMaps: false, generateDepthBuffer: false, type: 2, samplingMode: 2 }); this._taaThinPostProcess.textureWidth = width; this._taaThinPostProcess.textureHeight = height; } _updateReprojection(reproject) { if (reproject) { if (!this._scene.enablePrePassRenderer()) { Logger.Warn("TAA reprojection requires PrePass which is not supported"); return; } else if (!this._taaMaterialManager) { this._taaMaterialManager = new TAAMaterialManager(this._scene); } // The velocity buffer may be old so reset for one frame this._taaThinPostProcess._reset(); } this._taaThinPostProcess.reprojectHistory = reproject; if (this._taaMaterialManager) { this._taaMaterialManager.isEnabled = reproject && this._isEnabled; } this._buildPipeline(); } _buildPipeline() { if (!this.isSupported) { return; } if (!this._isEnabled) { this._isDirty = true; return; } this._isDirty = false; const engine = this._scene.getEngine(); this._disposePostProcesses(); if (this._cameras !== null) { this._scene.postProcessRenderPipelineManager.detachCamerasFromRenderPipeline(this._name, this._cameras); // get back cameras to be used to reattach pipeline this._cameras = this._camerasToBeAttached.slice(); } this._reset(); this._createTAAPostProcess(); this.addEffect(new PostProcessRenderEffect(engine, this.TAARenderEffect, () => { return this._taaPostProcess; }, true)); this._createPassPostProcess(); this.addEffect(new PostProcessRenderEffect(engine, this.TAAPassEffect, () => { return this._passPostProcess; }, true)); if (this._cameras !== null) { this._scene.postProcessRenderPipelineManager.attachCamerasToRenderPipeline(this._name, this._cameras); } } _disposePostProcesses() { for (let i = 0; i < this._cameras.length; i++) { const camera = this._cameras[i]; this._taaPostProcess?.dispose(camera); this._passPostProcess?.dispose(camera); camera.getProjectionMatrix(true); // recompute the projection matrix } this._taaPostProcess = null; this._passPostProcess = null; } _createTAAPostProcess() { this._taaPostProcess = new PostProcess("TAA", "taa", { uniforms: ["factor"], samplers: ["historySampler"], size: 1.0, engine: this._scene.getEngine(), textureType: this._textureType, effectWrapper: this._taaThinPostProcess, }); if (this.reprojectHistory) { this._taaPostProcess._prePassEffectConfiguration = new TAAEffectConfiguration(); } this._taaPostProcess.samples = this._msaaSamples; this._taaPostProcess.onActivateObservable.add(() => { this._taaThinPostProcess.camera = this._scene.activeCamera; if (this._taaPostProcess?.width !== this._ping.width || this._taaPostProcess?.height !== this._ping.height) { const engine = this._scene.getEngine(); this._createPingPongTextures(engine.getRenderWidth(), engine.getRenderHeight()); } if (this.reprojectHistory && this._taaMaterialManager) { // Applying jitter to the projection matrix messes with the velocity buffer, // so we do it as a final vertex step in a material plugin instead this._taaThinPostProcess.nextJitterOffset(this._taaMaterialManager.jitter); } else { // Use the projection matrix by default since it supports most materials this._taaThinPostProcess.updateProjectionMatrix(); } if (this._passPostProcess) { this._passPostProcess.inputTexture = this._pingpong ? this._ping : this._pong; } this._pingpong = this._pingpong ^ 1; }); this._taaPostProcess.onApplyObservable.add((effect) => { effect._bindTexture("historySampler", this._pingpong ? this._ping.texture : this._pong.texture); const prePassRenderer = this._scene.prePassRenderer; if (this.reprojectHistory && prePassRenderer) { const renderTarget = prePassRenderer.getRenderTarget(); const velocityIndex = prePassRenderer.getIndex(11); effect.setTexture("velocitySampler", renderTarget.textures[velocityIndex]); } }); } _createPassPostProcess() { const engine = this._scene.getEngine(); this._passPostProcess = new PassPostProcess("TAAPass", 1, null, 1, engine); this._passPostProcess.inputTexture = this._ping; this._passPostProcess.autoClear = false; } /** * Serializes the rendering pipeline (Used when exporting) * @returns the serialized object */ serialize() { const serializationObject = SerializationHelper.Serialize(this); serializationObject.customType = "TAARenderingPipeline"; return serializationObject; } /** * Parse the serialized pipeline * @param source Source pipeline. * @param scene The scene to load the pipeline to. * @param rootUrl The URL of the serialized pipeline. * @returns An instantiated pipeline from the serialized object. */ static Parse(source, scene, rootUrl) { return SerializationHelper.Parse(() => new TAARenderingPipeline(source._name, scene, source._ratio), source, scene, rootUrl); } } __decorate([ serialize("samples") ], TAARenderingPipeline.prototype, "samples", null); __decorate([ serialize("msaaSamples") ], TAARenderingPipeline.prototype, "_msaaSamples", void 0); __decorate([ serialize() ], TAARenderingPipeline.prototype, "factor", null); __decorate([ serialize() ], TAARenderingPipeline.prototype, "disableOnCameraMove", null); __decorate([ serialize() ], TAARenderingPipeline.prototype, "reprojectHistory", null); __decorate([ serialize() ], TAARenderingPipeline.prototype, "clampHistory", null); __decorate([ serialize("isEnabled") ], TAARenderingPipeline.prototype, "_isEnabled", void 0); RegisterClass("BABYLON.TAARenderingPipeline", TAARenderingPipeline); //# sourceMappingURL=taaRenderingPipeline.js.map