From fc34d0774ac04d5d4cc7743e12c8cd782e22e8f8 Mon Sep 17 00:00:00 2001 From: Popov72 Date: Thu, 7 Nov 2024 17:25:53 +0100 Subject: [PATCH 1/3] Add TAA task to frame graph --- .../Rendering/baseObjectRendererBlock.ts | 180 +++++++++++++++++ .../Blocks/Rendering/objectRendererBlock.ts | 159 +-------------- .../Rendering/taaObjectRendererBlock.ts | 108 +++++++++++ .../core/src/FrameGraph/Node/Blocks/index.ts | 9 +- .../core/src/FrameGraph/Passes/renderPass.ts | 2 +- .../Tasks/Rendering/taaObjectRendererTask.ts | 146 ++++++++++++++ .../dev/core/src/FrameGraph/frameGraph.ts | 20 +- .../src/FrameGraph/frameGraphRenderContext.ts | 14 +- .../dev/core/src/FrameGraph/frameGraphTask.ts | 8 +- .../FrameGraph/frameGraphTextureManager.ts | 83 ++++++-- .../core/src/FrameGraph/frameGraphTypes.ts | 3 + packages/dev/core/src/FrameGraph/index.ts | 5 +- .../Pipelines/taaRenderingPipeline.ts | 74 +++---- .../src/PostProcesses/thinTAAPostProcess.ts | 181 ++++++++++++++++++ .../dev/core/src/ShadersWGSL/taa.fragment.fx | 11 ++ .../nodeRenderGraphEditor/src/blockTools.ts | 4 + .../components/nodeList/nodeListComponent.tsx | 3 +- .../src/components/preview/previewManager.ts | 2 +- 18 files changed, 784 insertions(+), 228 deletions(-) create mode 100644 packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/baseObjectRendererBlock.ts create mode 100644 packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/taaObjectRendererBlock.ts create mode 100644 packages/dev/core/src/FrameGraph/Tasks/Rendering/taaObjectRendererTask.ts create mode 100644 packages/dev/core/src/PostProcesses/thinTAAPostProcess.ts create mode 100644 packages/dev/core/src/ShadersWGSL/taa.fragment.fx diff --git a/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/baseObjectRendererBlock.ts b/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/baseObjectRendererBlock.ts new file mode 100644 index 00000000000..e4dc7c05132 --- /dev/null +++ b/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/baseObjectRendererBlock.ts @@ -0,0 +1,180 @@ +// eslint-disable-next-line import/no-internal-modules +import type { NodeRenderGraphConnectionPoint, Scene, NodeRenderGraphBuildState, FrameGraph, FrameGraphTextureHandle, FrameGraphObjectList, Camera } from "core/index"; +import { NodeRenderGraphBlock } from "../../nodeRenderGraphBlock"; +import { NodeRenderGraphBlockConnectionPointTypes } from "../../Types/nodeRenderGraphTypes"; +import { editableInPropertyPage, PropertyTypeForEdition } from "../../../../Decorators/nodeDecorator"; +import type { FrameGraphObjectRendererTask } from "../../../Tasks/Rendering/objectRendererTask"; + +/** + * @internal + */ +export class NodeRenderGraphBaseObjectRendererBlock extends NodeRenderGraphBlock { + protected override _frameGraphTask: FrameGraphObjectRendererTask; + + /** + * Gets the frame graph task associated with this block + */ + public override get task() { + return this._frameGraphTask; + } + + /** + * Create a new NodeRenderGraphBaseObjectRendererBlock + * @param name defines the block name + * @param frameGraph defines the hosting frame graph + * @param scene defines the hosting scene + */ + public constructor(name: string, frameGraph: FrameGraph, scene: Scene) { + super(name, frameGraph, scene); + + this.registerInput("destination", NodeRenderGraphBlockConnectionPointTypes.Texture); + this.registerInput("depth", NodeRenderGraphBlockConnectionPointTypes.TextureBackBufferDepthStencilAttachment, true); + this.registerInput("camera", NodeRenderGraphBlockConnectionPointTypes.Camera); + this.registerInput("objects", NodeRenderGraphBlockConnectionPointTypes.ObjectList); + this.registerInput("dependencies", NodeRenderGraphBlockConnectionPointTypes.Texture, true); + + this.registerOutput("output", NodeRenderGraphBlockConnectionPointTypes.BasedOnInput); + this.registerOutput("outputDepth", NodeRenderGraphBlockConnectionPointTypes.BasedOnInput); + + this.destination.addAcceptedConnectionPointTypes(NodeRenderGraphBlockConnectionPointTypes.TextureAllButBackBufferDepthStencil); + this.depth.addAcceptedConnectionPointTypes(NodeRenderGraphBlockConnectionPointTypes.TextureDepthStencilAttachment); + this.dependencies.addAcceptedConnectionPointTypes(NodeRenderGraphBlockConnectionPointTypes.TextureAllButBackBuffer); + + this.output._typeConnectionSource = this.destination; + this.outputDepth._typeConnectionSource = this.depth; + } + + /** Indicates if depth testing must be enabled or disabled */ + @editableInPropertyPage("Depth test", PropertyTypeForEdition.Boolean, "PROPERTIES") + public get depthTest() { + return this._frameGraphTask.depthTest; + } + + public set depthTest(value: boolean) { + this._frameGraphTask.depthTest = value; + } + + /** Indicates if depth writing must be enabled or disabled */ + @editableInPropertyPage("Depth write", PropertyTypeForEdition.Boolean, "PROPERTIES") + public get depthWrite() { + return this._frameGraphTask.depthWrite; + } + + public set depthWrite(value: boolean) { + this._frameGraphTask.depthWrite = value; + } + + /** + * Gets the current class name + * @returns the class name + */ + public override getClassName() { + return "NodeRenderGraphBaseObjectRendererBlock"; + } + + /** + * Gets the destination texture input component + */ + public get destination(): NodeRenderGraphConnectionPoint { + return this._inputs[0]; + } + + /** + * Gets the depth texture input component + */ + public get depth(): NodeRenderGraphConnectionPoint { + return this._inputs[1]; + } + + /** + * Gets the camera input component + */ + public get camera(): NodeRenderGraphConnectionPoint { + return this._inputs[2]; + } + + /** + * Gets the objects input component + */ + public get objects(): NodeRenderGraphConnectionPoint { + return this._inputs[3]; + } + + /** + * Gets the dependencies input component + */ + public get dependencies(): NodeRenderGraphConnectionPoint { + return this._inputs[4]; + } + + /** + * Gets the output component + */ + public get output(): NodeRenderGraphConnectionPoint { + return this._outputs[0]; + } + + /** + * Gets the output depth component + */ + public get outputDepth(): NodeRenderGraphConnectionPoint { + return this._outputs[1]; + } + + protected override _buildBlock(state: NodeRenderGraphBuildState) { + super._buildBlock(state); + + this._frameGraphTask.name = this.name; + + this.output.value = this._frameGraphTask.outputTexture; // the value of the output connection point is the "output" texture of the task + + this.outputDepth.value = this._frameGraphTask.outputDepthTexture; // the value of the outputDepth connection point is the "outputDepth" texture of the task + + const destinationConnectedPoint = this.destination.connectedPoint; + if (destinationConnectedPoint) { + this._frameGraphTask.destinationTexture = destinationConnectedPoint.value as FrameGraphTextureHandle; + } + + const depthConnectedPoint = this.depth.connectedPoint; + if (depthConnectedPoint) { + this._frameGraphTask.depthTexture = depthConnectedPoint.value as FrameGraphTextureHandle; + } + + const cameraConnectedPoint = this.camera.connectedPoint; + if (cameraConnectedPoint) { + this._frameGraphTask.camera = cameraConnectedPoint.value as Camera; + } + + const objectsConnectedPoint = this.objects.connectedPoint; + if (objectsConnectedPoint) { + this._frameGraphTask.objectList = objectsConnectedPoint.value as FrameGraphObjectList; + } + + this._frameGraphTask.dependencies = []; + + const dependenciesConnectedPoint = this.dependencies.connectedPoint; + if (dependenciesConnectedPoint) { + this._frameGraphTask.dependencies[0] = dependenciesConnectedPoint.value as FrameGraphTextureHandle; + } + } + + protected override _dumpPropertiesCode() { + const codes: string[] = []; + codes.push(`${this._codeVariableName}.depthTest = ${this.depthTest};`); + codes.push(`${this._codeVariableName}.depthWrite = ${this.depthWrite};`); + return super._dumpPropertiesCode() + codes.join("\n"); + } + + public override serialize(): any { + const serializationObject = super.serialize(); + serializationObject.depthTest = this.depthTest; + serializationObject.depthWrite = this.depthWrite; + return serializationObject; + } + + public override _deserialize(serializationObject: any) { + super._deserialize(serializationObject); + this.depthTest = serializationObject.depthTest; + this.depthWrite = serializationObject.depthWrite; + } +} diff --git a/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/objectRendererBlock.ts b/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/objectRendererBlock.ts index f6a80dce126..7208c021d4f 100644 --- a/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/objectRendererBlock.ts +++ b/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/objectRendererBlock.ts @@ -1,24 +1,13 @@ // eslint-disable-next-line import/no-internal-modules -import type { NodeRenderGraphConnectionPoint, Scene, NodeRenderGraphBuildState, FrameGraph, FrameGraphTextureHandle, FrameGraphObjectList, Camera } from "core/index"; -import { NodeRenderGraphBlock } from "../../nodeRenderGraphBlock"; +import type { Scene, FrameGraph } from "core/index"; import { RegisterClass } from "../../../../Misc/typeStore"; -import { NodeRenderGraphBlockConnectionPointTypes } from "../../Types/nodeRenderGraphTypes"; -import { editableInPropertyPage, PropertyTypeForEdition } from "../../../../Decorators/nodeDecorator"; import { FrameGraphObjectRendererTask } from "../../../Tasks/Rendering/objectRendererTask"; +import { NodeRenderGraphBaseObjectRendererBlock } from "./baseObjectRendererBlock"; /** * Block that render objects to a render target */ -export class NodeRenderGraphObjectRendererBlock extends NodeRenderGraphBlock { - protected override _frameGraphTask: FrameGraphObjectRendererTask; - - /** - * Gets the frame graph task associated with this block - */ - public override get task() { - return this._frameGraphTask; - } - +export class NodeRenderGraphObjectRendererBlock extends NodeRenderGraphBaseObjectRendererBlock { /** * Create a new NodeRenderGraphObjectRendererBlock * @param name defines the block name @@ -28,45 +17,9 @@ export class NodeRenderGraphObjectRendererBlock extends NodeRenderGraphBlock { public constructor(name: string, frameGraph: FrameGraph, scene: Scene) { super(name, frameGraph, scene); - this.registerInput("destination", NodeRenderGraphBlockConnectionPointTypes.Texture); - this.registerInput("depth", NodeRenderGraphBlockConnectionPointTypes.TextureBackBufferDepthStencilAttachment, true); - this.registerInput("camera", NodeRenderGraphBlockConnectionPointTypes.Camera); - this.registerInput("objects", NodeRenderGraphBlockConnectionPointTypes.ObjectList); - this.registerInput("dependencies", NodeRenderGraphBlockConnectionPointTypes.Texture, true); - - this.registerOutput("output", NodeRenderGraphBlockConnectionPointTypes.BasedOnInput); - this.registerOutput("outputDepth", NodeRenderGraphBlockConnectionPointTypes.BasedOnInput); - - this.destination.addAcceptedConnectionPointTypes(NodeRenderGraphBlockConnectionPointTypes.TextureAllButBackBufferDepthStencil); - this.depth.addAcceptedConnectionPointTypes(NodeRenderGraphBlockConnectionPointTypes.TextureDepthStencilAttachment); - this.dependencies.addAcceptedConnectionPointTypes(NodeRenderGraphBlockConnectionPointTypes.TextureAllButBackBuffer); - - this.output._typeConnectionSource = this.destination; - this.outputDepth._typeConnectionSource = this.depth; - this._frameGraphTask = new FrameGraphObjectRendererTask(this.name, frameGraph, scene); } - /** Indicates if depth testing must be enabled or disabled */ - @editableInPropertyPage("Depth test", PropertyTypeForEdition.Boolean, "PROPERTIES") - public get depthTest() { - return this._frameGraphTask.depthTest; - } - - public set depthTest(value: boolean) { - this._frameGraphTask.depthTest = value; - } - - /** Indicates if depth writing must be enabled or disabled */ - @editableInPropertyPage("Depth write", PropertyTypeForEdition.Boolean, "PROPERTIES") - public get depthWrite() { - return this._frameGraphTask.depthWrite; - } - - public set depthWrite(value: boolean) { - this._frameGraphTask.depthWrite = value; - } - /** * Gets the current class name * @returns the class name @@ -74,112 +27,6 @@ export class NodeRenderGraphObjectRendererBlock extends NodeRenderGraphBlock { public override getClassName() { return "NodeRenderGraphObjectRendererBlock"; } - - /** - * Gets the destination texture input component - */ - public get destination(): NodeRenderGraphConnectionPoint { - return this._inputs[0]; - } - - /** - * Gets the depth texture input component - */ - public get depth(): NodeRenderGraphConnectionPoint { - return this._inputs[1]; - } - - /** - * Gets the camera input component - */ - public get camera(): NodeRenderGraphConnectionPoint { - return this._inputs[2]; - } - - /** - * Gets the objects input component - */ - public get objects(): NodeRenderGraphConnectionPoint { - return this._inputs[3]; - } - - /** - * Gets the dependencies input component - */ - public get dependencies(): NodeRenderGraphConnectionPoint { - return this._inputs[4]; - } - - /** - * Gets the output component - */ - public get output(): NodeRenderGraphConnectionPoint { - return this._outputs[0]; - } - - /** - * Gets the output depth component - */ - public get outputDepth(): NodeRenderGraphConnectionPoint { - return this._outputs[1]; - } - - protected override _buildBlock(state: NodeRenderGraphBuildState) { - super._buildBlock(state); - - this._frameGraphTask.name = this.name; - - this.output.value = this._frameGraphTask.outputTexture; // the value of the output connection point is the "output" texture of the task - - this.outputDepth.value = this._frameGraphTask.outputDepthTexture; // the value of the outputDepth connection point is the "outputDepth" texture of the task - - const destinationConnectedPoint = this.destination.connectedPoint; - if (destinationConnectedPoint) { - this._frameGraphTask.destinationTexture = destinationConnectedPoint.value as FrameGraphTextureHandle; - } - - const depthConnectedPoint = this.depth.connectedPoint; - if (depthConnectedPoint) { - this._frameGraphTask.depthTexture = depthConnectedPoint.value as FrameGraphTextureHandle; - } - - const cameraConnectedPoint = this.camera.connectedPoint; - if (cameraConnectedPoint) { - this._frameGraphTask.camera = cameraConnectedPoint.value as Camera; - } - - const objectsConnectedPoint = this.objects.connectedPoint; - if (objectsConnectedPoint) { - this._frameGraphTask.objectList = objectsConnectedPoint.value as FrameGraphObjectList; - } - - this._frameGraphTask.dependencies = []; - - const dependenciesConnectedPoint = this.dependencies.connectedPoint; - if (dependenciesConnectedPoint) { - this._frameGraphTask.dependencies[0] = dependenciesConnectedPoint.value as FrameGraphTextureHandle; - } - } - - protected override _dumpPropertiesCode() { - const codes: string[] = []; - codes.push(`${this._codeVariableName}.depthTest = ${this.depthTest};`); - codes.push(`${this._codeVariableName}.depthWrite = ${this.depthWrite};`); - return super._dumpPropertiesCode() + codes.join("\n"); - } - - public override serialize(): any { - const serializationObject = super.serialize(); - serializationObject.depthTest = this.depthTest; - serializationObject.depthWrite = this.depthWrite; - return serializationObject; - } - - public override _deserialize(serializationObject: any) { - super._deserialize(serializationObject); - this.depthTest = serializationObject.depthTest; - this.depthWrite = serializationObject.depthWrite; - } } RegisterClass("BABYLON.NodeRenderGraphObjectRendererBlock", NodeRenderGraphObjectRendererBlock); diff --git a/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/taaObjectRendererBlock.ts b/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/taaObjectRendererBlock.ts new file mode 100644 index 00000000000..7439fa2f21a --- /dev/null +++ b/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/taaObjectRendererBlock.ts @@ -0,0 +1,108 @@ +// eslint-disable-next-line import/no-internal-modules +import type { Scene, FrameGraph } from "core/index"; +import { RegisterClass } from "../../../../Misc/typeStore"; +import { editableInPropertyPage, PropertyTypeForEdition } from "../../../../Decorators/nodeDecorator"; +import { FrameGraphTAAObjectRendererTask } from "core/FrameGraph/Tasks/Rendering/taaObjectRendererTask"; +import { NodeRenderGraphBaseObjectRendererBlock } from "./baseObjectRendererBlock"; + +/** + * Block that render objects with temporal anti-aliasing to a render target + */ +export class NodeRenderGraphTAAObjectRendererBlock extends NodeRenderGraphBaseObjectRendererBlock { + protected override _frameGraphTask: FrameGraphTAAObjectRendererTask; + + /** + * Gets the frame graph task associated with this block + */ + public override get task() { + return this._frameGraphTask; + } + + /** + * Create a new NodeRenderGraphTAAObjectRendererBlock + * @param name defines the block name + * @param frameGraph defines the hosting frame graph + * @param scene defines the hosting scene + */ + public constructor(name: string, frameGraph: FrameGraph, scene: Scene) { + super(name, frameGraph, scene); + + this._frameGraphTask = new FrameGraphTAAObjectRendererTask(this.name, frameGraph, scene); + } + + /** Number of accumulated samples */ + @editableInPropertyPage("Samples", PropertyTypeForEdition.Int, "TEMPORAL ANTI-ALIASING") + public get samples() { + return this._frameGraphTask.postProcess.samples; + } + + public set samples(value: number) { + this._frameGraphTask.postProcess.samples = value; + } + + /** The factor used to blend the history frame with current frame */ + @editableInPropertyPage("Factor", PropertyTypeForEdition.Float, "TEMPORAL ANTI-ALIASING") + public get factor() { + return this._frameGraphTask.postProcess.factor; + } + + public set factor(value: number) { + this._frameGraphTask.postProcess.factor = value; + } + + /** Indicates if depth testing must be enabled or disabled */ + @editableInPropertyPage("Disable on camera move", PropertyTypeForEdition.Boolean, "TEMPORAL ANTI-ALIASING") + public get disableOnCameraMove() { + return this._frameGraphTask.postProcess.disableOnCameraMove; + } + + public set disableOnCameraMove(value: boolean) { + this._frameGraphTask.postProcess.disableOnCameraMove = value; + } + + /** Indicates if TAA must be enabled or disabled */ + @editableInPropertyPage("Disable TAA", PropertyTypeForEdition.Boolean, "TEMPORAL ANTI-ALIASING") + public get disableTAA() { + return this._frameGraphTask.postProcess.disabled; + } + + public set disableTAA(value: boolean) { + this._frameGraphTask.postProcess.disabled = value; + } + + /** + * Gets the current class name + * @returns the class name + */ + public override getClassName() { + return "NodeRenderGraphTAAObjectRendererBlock"; + } + + protected override _dumpPropertiesCode() { + const codes: string[] = []; + codes.push(`${this._codeVariableName}.samples = ${this.samples};`); + codes.push(`${this._codeVariableName}.factor = ${this.factor};`); + codes.push(`${this._codeVariableName}.disableOnCameraMove = ${this.disableOnCameraMove};`); + codes.push(`${this._codeVariableName}.disableTAA = ${this.disableTAA};`); + return super._dumpPropertiesCode() + codes.join("\n"); + } + + public override serialize(): any { + const serializationObject = super.serialize(); + serializationObject.samples = this.samples; + serializationObject.factor = this.factor; + serializationObject.disableOnCameraMove = this.disableOnCameraMove; + serializationObject.disableTAA = this.disableTAA; + return serializationObject; + } + + public override _deserialize(serializationObject: any) { + super._deserialize(serializationObject); + this.samples = serializationObject.samples; + this.factor = serializationObject.factor; + this.disableOnCameraMove = serializationObject.disableOnCameraMove; + this.disableTAA = serializationObject.disableTAA; + } +} + +RegisterClass("BABYLON.NodeRenderGraphTAAObjectRendererBlock", NodeRenderGraphTAAObjectRendererBlock); diff --git a/packages/dev/core/src/FrameGraph/Node/Blocks/index.ts b/packages/dev/core/src/FrameGraph/Node/Blocks/index.ts index c3b66891140..d3cc8f99cea 100644 --- a/packages/dev/core/src/FrameGraph/Node/Blocks/index.ts +++ b/packages/dev/core/src/FrameGraph/Node/Blocks/index.ts @@ -1,17 +1,22 @@ export * from "./elbowBlock"; export * from "./inputBlock"; export * from "./outputBlock"; + export * from "./PostProcesses/blackAndWhitePostProcessBlock"; export * from "./PostProcesses/bloomPostProcessBlock"; export * from "./PostProcesses/blurPostProcessBlock"; export * from "./PostProcesses/circleOfConfusionPostProcessBlock"; export * from "./PostProcesses/depthOfFieldPostProcessBlock"; export * from "./PostProcesses/extractHighlightsPostProcessBlock"; -export * from "./Rendering/objectRendererBlock"; -export * from "./Rendering/geometryRendererBlock"; + export * from "./Rendering/cullObjectsBlock"; +export * from "./Rendering/geometryRendererBlock"; +export * from "./Rendering/objectRendererBlock"; +export * from "./Rendering/taaObjectRendererBlock"; + export * from "./Teleport/teleportInBlock"; export * from "./Teleport/teleportOutBlock"; + export * from "./Textures/clearBlock"; export * from "./Textures/copyTextureBlock"; export * from "./Textures/generateMipmapsBlock"; diff --git a/packages/dev/core/src/FrameGraph/Passes/renderPass.ts b/packages/dev/core/src/FrameGraph/Passes/renderPass.ts index ed048267415..eac30164f18 100644 --- a/packages/dev/core/src/FrameGraph/Passes/renderPass.ts +++ b/packages/dev/core/src/FrameGraph/Passes/renderPass.ts @@ -74,7 +74,7 @@ export class FrameGraphRenderPass extends FrameGraphPass { + this._rtt.renderList = this.objectList.meshes; + this._rtt.particleSystemList = this.objectList.particleSystems; + + this._scene.incrementRenderId(); + this._scene.resetCachedMaterial(); + + this.postProcess.updateProjectionMatrix(); + + context.setDepthStates(this.depthTest && depthEnabled, this.depthWrite && depthEnabled); + + // We define the active camera and transformation matrices ourselves, otherwise this will be done by calling context.render, in which case + // getProjectionMatrix will be called with a "true" parameter, forcing recalculation of the projection matrix and losing our changes. + if (!this.postProcess.disabled) { + this._scene.activeCamera = this.camera; + this._scene.setTransformMatrix(this.camera.getViewMatrix(), this.camera.getProjectionMatrix()); + } + + context.render(this._rtt); + + this._scene.activeCamera = null; + + context.bindRenderTarget(pingPongHandle, "frame graph - TAA merge with history texture"); + + if (!this.postProcess.disabled) { + context.applyFullScreenEffect(this._postProcessDrawWrapper, () => { + this.postProcess.bind(); + context.bindTextureHandle(this._postProcessDrawWrapper.effect!, "textureSampler", this.destinationTexture); + context.bindTextureHandle(this._postProcessDrawWrapper.effect!, "historySampler", pingPongHandle); + }); + } else { + context.copyTexture(this.destinationTexture); + } + }); + + const passDisabled = this._frameGraph.addRenderPass(this.name + "_disabled", true); + + passDisabled.setRenderTarget(this.outputTexture); + if (this.depthTexture !== undefined) { + passDisabled.setRenderTargetDepth(this.depthTexture); + } + passDisabled.setExecuteFunc((context) => { + context.copyTexture(this.destinationTexture); + }); + + if (this.dependencies !== undefined) { + for (const handle of this.dependencies) { + pass.useTexture(handle); + passDisabled.useTexture(handle); + } + } + + return pass; + } +} diff --git a/packages/dev/core/src/FrameGraph/frameGraph.ts b/packages/dev/core/src/FrameGraph/frameGraph.ts index 655dd3ee6ac..40bb84a387c 100644 --- a/packages/dev/core/src/FrameGraph/frameGraph.ts +++ b/packages/dev/core/src/FrameGraph/frameGraph.ts @@ -183,7 +183,9 @@ export class FrameGraph { * Executes the frame graph. */ public execute(): void { - this._renderContext._bindRenderTarget(); + this._renderContext.bindRenderTarget(); + + this._textureManager.updateHistoryTextures(); for (const task of this._tasks) { const passes = task._getPasses(); @@ -313,6 +315,22 @@ export class FrameGraph { this._textureManager.resolveDanglingHandle(danglingHandle, handle); } + /** + * Checks if a handle is a history texture (or points to a history texture, for a dangling handle) + * @param handle The handle to check + * @returns True if the handle is a history texture, otherwise false + */ + public isHistoryTexture(handle: FrameGraphTextureHandle): boolean { + const entry = this._textureManager._textures.get(handle); + if (!entry) { + return false; + } + + handle = entry.refHandle ?? handle; + + return this._textureManager._historyTextures.has(handle); + } + /** * Clears the frame graph (remove the tasks and release the textures). * The frame graph can be built again after this method is called. diff --git a/packages/dev/core/src/FrameGraph/frameGraphRenderContext.ts b/packages/dev/core/src/FrameGraph/frameGraphRenderContext.ts index c9b64a271cb..4a0ed911997 100644 --- a/packages/dev/core/src/FrameGraph/frameGraphRenderContext.ts +++ b/packages/dev/core/src/FrameGraph/frameGraphRenderContext.ts @@ -181,7 +181,7 @@ export class FrameGraphRenderContext extends FrameGraphContext { */ public copyTexture(sourceTexture: FrameGraphTextureHandle, forceCopyToBackbuffer = false): void { if (forceCopyToBackbuffer) { - this._bindRenderTarget(); + this.bindRenderTarget(); } this._applyRenderTarget(); this._copyTexture.copy(this._textureManager.getTextureFromHandle(sourceTexture)!.texture!); @@ -200,9 +200,10 @@ export class FrameGraphRenderContext extends FrameGraphContext { * Binds a render target texture so that upcoming draw calls will render to it * Note: it is a lazy operation, so the render target will only be bound when needed. This way, it is possible to call * this method several times with different render targets without incurring the cost of binding if no draw calls are made - * @internal + * @param renderTargetHandle The handle of the render target texture to bind (default: backbufferColorTextureHandle) + * @param debugMessage Optional debug message to display when the render target is bound (visible in PIX, for example) */ - public _bindRenderTarget(renderTargetHandle: FrameGraphTextureHandle = backbufferColorTextureHandle, debugMessage?: string) { + public bindRenderTarget(renderTargetHandle: FrameGraphTextureHandle = backbufferColorTextureHandle, debugMessage?: string) { if (renderTargetHandle === this._currentRenderTargetHandle) { this._flushDebugMessages(); if (debugMessage !== undefined) { @@ -243,7 +244,12 @@ export class FrameGraphRenderContext extends FrameGraphContext { const handle = this._currentRenderTargetHandle; const textureSlot = this._textureManager._textures.get(handle)!; - const renderTarget = textureSlot.texture; + let renderTarget = textureSlot.texture; + + if (textureSlot.creationOptions.isHistoryTexture) { + const historyEntry = this._textureManager._historyTextures.get(textureSlot.refHandle ?? handle)!; + renderTarget = historyEntry.textures[historyEntry.index]; + } this._flushDebugMessages(); diff --git a/packages/dev/core/src/FrameGraph/frameGraphTask.ts b/packages/dev/core/src/FrameGraph/frameGraphTask.ts index f9c7073f22b..0f1ed969eba 100644 --- a/packages/dev/core/src/FrameGraph/frameGraphTask.ts +++ b/packages/dev/core/src/FrameGraph/frameGraphTask.ts @@ -1,5 +1,5 @@ // eslint-disable-next-line import/no-internal-modules -import type { RenderTargetWrapper, FrameGraph, FrameGraphObjectList, IFrameGraphPass, Nullable } from "core/index"; +import type { RenderTargetWrapper, FrameGraph, FrameGraphObjectList, IFrameGraphPass, Nullable, FrameGraphTextureHandle } from "core/index"; import { FrameGraphCullPass } from "./Passes/cullPass"; import { FrameGraphRenderPass } from "./Passes/renderPass"; @@ -107,6 +107,7 @@ export abstract class FrameGraphTask { } let disabledOutputTexture: Nullable = null; + let disabledOutputTextureHandle: FrameGraphTextureHandle = -1; let disabledOutputDepthTexture: Nullable = null; let disabledOutputObjectList: FrameGraphObjectList | undefined; @@ -117,6 +118,7 @@ export abstract class FrameGraphTask { } if (FrameGraphRenderPass.IsRenderPass(pass)) { disabledOutputTexture = this._frameGraph.getTexture(pass.renderTarget); + disabledOutputTextureHandle = pass.renderTarget; disabledOutputDepthTexture = pass.renderTargetDepth !== undefined ? this._frameGraph.getTexture(pass.renderTargetDepth) : null; } else if (FrameGraphCullPass.IsCullPass(pass)) { disabledOutputObjectList = pass.objectList; @@ -125,7 +127,9 @@ export abstract class FrameGraphTask { if (this._passesDisabled.length > 0) { if (outputTexture !== disabledOutputTexture) { - throw new Error(`The output texture of the task "${this.name}" is different when it is enabled or disabled.`); + if (!this._frameGraph.isHistoryTexture(disabledOutputTextureHandle)) { + throw new Error(`The output texture of the task "${this.name}" is different when it is enabled or disabled.`); + } } if (outputDepthTexture !== disabledOutputDepthTexture) { throw new Error(`The output depth texture of the task "${this.name}" is different when it is enabled or disabled.`); diff --git a/packages/dev/core/src/FrameGraph/frameGraphTextureManager.ts b/packages/dev/core/src/FrameGraph/frameGraphTextureManager.ts index c5180d7db5b..6bab18a7d0c 100644 --- a/packages/dev/core/src/FrameGraph/frameGraphTextureManager.ts +++ b/packages/dev/core/src/FrameGraph/frameGraphTextureManager.ts @@ -14,15 +14,22 @@ import { Texture } from "../Materials/Textures/texture"; import { backbufferColorTextureHandle, backbufferDepthStencilTextureHandle } from "./frameGraphTypes"; import { Constants } from "../Engines/constants"; +type HistoryTexture = { + textures: Array>; + handles: Array; + index: number; + refHandles: Array; // (dangling) handles that reference this history texture +}; + type TextureEntry = { texture: Nullable; name: string; creationOptions: FrameGraphTextureCreationOptions; namespace: FrameGraphTextureNamespace; debug?: Texture; - parentHandle?: FrameGraphTextureHandle; - parentTextureIndex?: number; - refHandle?: FrameGraphTextureHandle; + parentHandle?: FrameGraphTextureHandle; // Parent handle if the texture comes from a multi-target texture + parentTextureIndex?: number; // Index of the texture in the parent if the texture comes from a multi-target texture + refHandle?: FrameGraphTextureHandle; // Handle of the texture this one is referencing - used for dangling handles }; enum FrameGraphTextureNamespace { @@ -39,6 +46,7 @@ export class FrameGraphTextureManager { private static _Counter = 2; // 0 and 1 are reserved for backbuffer textures public _textures: Map = new Map(); + public _historyTextures: Map = new Map(); constructor( private _engine: AbstractEngine, @@ -92,6 +100,10 @@ export class FrameGraphTextureManager { } public getTextureFromHandle(handle: FrameGraphTextureHandle): Nullable { + const historyEntry = this._historyTextures.get(handle); + if (historyEntry) { + return historyEntry.textures[historyEntry.index ^ 1]; // get the read texture + } return this._textures.get(handle)!.texture; } @@ -99,7 +111,7 @@ export class FrameGraphTextureManager { const internalTexture = texture.texture; if (!internalTexture) { - throw new Error("Texture must have an internal texture to be imported"); + throw new Error("importTexture: Texture must have an internal texture to be imported"); } if (handle !== undefined) { @@ -165,6 +177,16 @@ export class FrameGraphTextureManager { }; } + public updateHistoryTextures(): void { + this._historyTextures.forEach((entry) => { + entry.index = entry.index ^ 1; + for (const refHandle of entry.refHandles) { + const textureEntry = this._textures.get(refHandle)!; + textureEntry.texture = entry.textures[entry.index]; + } + }); + } + public dispose(): void { this.releaseTextures(); } @@ -221,6 +243,12 @@ export class FrameGraphTextureManager { entry.debug = this._createDebugTexture(entry.name, entry.texture); } }); + + this._historyTextures.forEach((entry) => { + for (let i = 0; i < entry.handles.length; i++) { + entry.textures[i] = this._textures.get(entry.handles[i])!.texture; + } + }); } public createDanglingHandle() { @@ -228,12 +256,12 @@ export class FrameGraphTextureManager { } public resolveDanglingHandle(danglingHandle: FrameGraphTextureHandle, handle: FrameGraphTextureHandle) { - if (!this._textures.has(handle)) { + const textureEntry = this._textures.get(handle); + + if (textureEntry === undefined) { throw new Error(`resolveDanglingHandle: Handle ${handle} does not exist!`); } - const textureEntry = this._textures.get(handle)!; - this._textures.set(danglingHandle, { texture: textureEntry.texture, refHandle: handle, @@ -242,11 +270,17 @@ export class FrameGraphTextureManager { size: { ...(textureEntry.creationOptions.size as { width: number; height: number; depth?: number; layers?: number }) }, options: { ...textureEntry.creationOptions.options, label: textureEntry.name }, sizeIsPercentage: textureEntry.creationOptions.sizeIsPercentage, + isHistoryTexture: false, }, namespace: textureEntry.namespace, parentHandle: textureEntry.parentHandle, parentTextureIndex: textureEntry.parentTextureIndex, }); + + const historyEntry = this._historyTextures.get(handle); + if (historyEntry) { + historyEntry.refHandles.push(danglingHandle); + } } public releaseTextures(releaseAll = true): void { @@ -268,8 +302,15 @@ export class FrameGraphTextureManager { } }); + this._historyTextures.forEach((entry) => { + for (let i = 0; i < entry.handles.length; i++) { + entry.textures[i] = null; + } + }); + if (releaseAll) { this._textures.clear(); + this._historyTextures.clear(); this._addSystemTextures(); } } @@ -335,23 +376,43 @@ export class FrameGraphTextureManager { ): FrameGraphTextureHandle { handle = handle ?? FrameGraphTextureManager._Counter++; - this._textures.set(handle, { + const textureName = creationOptions.isHistoryTexture ? `${name} - ping` : name; + + const textureEntry: TextureEntry = { texture, - name, + name: textureName, creationOptions: { size: getDimensionsFromTextureSize(creationOptions.size), - options: { ...creationOptions.options, label: name }, + options: { ...creationOptions.options, label: textureName }, sizeIsPercentage: creationOptions.sizeIsPercentage, + isHistoryTexture: creationOptions.isHistoryTexture, }, namespace, parentHandle, parentTextureIndex, - }); + }; + + this._textures.set(handle, textureEntry); if (namespace === FrameGraphTextureNamespace.External) { return handle; } + if (creationOptions.isHistoryTexture) { + const pongCreationOptions: FrameGraphTextureCreationOptions = { + size: { ...(textureEntry.creationOptions.size as { width: number; height: number }) }, + options: { ...textureEntry.creationOptions.options }, + sizeIsPercentage: textureEntry.creationOptions.sizeIsPercentage, + isHistoryTexture: false, + }; + + const pongTexture = this._createHandleForTexture(`${name} - pong`, null, pongCreationOptions, namespace, false); + + this._historyTextures.set(handle, { textures: [null, null], handles: [handle, pongTexture], index: 0, refHandles: [] }); + + return handle; + } + if (multiTargetMode) { const textureCount = creationOptions.options.textureCount ?? 1; for (let i = 0; i < textureCount; i++) { diff --git a/packages/dev/core/src/FrameGraph/frameGraphTypes.ts b/packages/dev/core/src/FrameGraph/frameGraphTypes.ts index 0b8a6068f7e..1eb9840e1dc 100644 --- a/packages/dev/core/src/FrameGraph/frameGraphTypes.ts +++ b/packages/dev/core/src/FrameGraph/frameGraphTypes.ts @@ -28,6 +28,9 @@ export type FrameGraphTextureCreationOptions = { /** If true, indicates that "size" is percentages relative to the screen size */ sizeIsPercentage: boolean; + + /** Indicates that the texture is a history texture */ + isHistoryTexture?: boolean; }; /** diff --git a/packages/dev/core/src/FrameGraph/index.ts b/packages/dev/core/src/FrameGraph/index.ts index 4642a537f86..d3f6e94c141 100644 --- a/packages/dev/core/src/FrameGraph/index.ts +++ b/packages/dev/core/src/FrameGraph/index.ts @@ -21,9 +21,10 @@ export * from "./Tasks/Texture/clearTextureTask"; export * from "./Tasks/Texture/copyToBackbufferColorTask"; export * from "./Tasks/Texture/copyToTextureTask"; -export * from "./Tasks/Rendering/objectRendererTask"; -export * from "./Tasks/Rendering/geometryRendererTask"; export * from "./Tasks/Rendering/cullObjectsTask"; +export * from "./Tasks/Rendering/geometryRendererTask"; +export * from "./Tasks/Rendering/objectRendererTask"; +export * from "./Tasks/Rendering/taaObjectRendererTask"; export * from "./frameGraph"; export * from "./frameGraphContext"; diff --git a/packages/dev/core/src/PostProcesses/RenderPipeline/Pipelines/taaRenderingPipeline.ts b/packages/dev/core/src/PostProcesses/RenderPipeline/Pipelines/taaRenderingPipeline.ts index d21502604cc..a4c03a654f2 100644 --- a/packages/dev/core/src/PostProcesses/RenderPipeline/Pipelines/taaRenderingPipeline.ts +++ b/packages/dev/core/src/PostProcesses/RenderPipeline/Pipelines/taaRenderingPipeline.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { serialize } from "../../../Misc/decorators"; import { SerializationHelper } from "../../../Misc/decorators.serialization"; -import { Camera } from "../../../Cameras/camera"; +import type { Camera } from "../../../Cameras/camera"; import type { Effect } from "../../../Materials/effect"; import { PostProcess } from "../../postProcess"; import { PostProcessRenderPipeline } from "../postProcessRenderPipeline"; @@ -12,12 +12,10 @@ import { Constants } from "../../../Engines/constants"; import type { Nullable } from "../../../types"; import { PassPostProcess } from "core/PostProcesses/passPostProcess"; import type { RenderTargetWrapper } from "core/Engines/renderTargetWrapper"; -import { Halton2DSequence } from "core/Maths/halton2DSequence"; +import { ThinTAAPostProcess } from "core/PostProcesses/thinTAAPostProcess"; import "../postProcessRenderPipelineManagerSceneComponent"; -import "../../../Shaders/taa.fragment"; - /** * Simple implementation of Temporal Anti-Aliasing (TAA). * This can be used to improve image quality for still pictures (screenshots for e.g.). @@ -32,22 +30,16 @@ export class TAARenderingPipeline extends PostProcessRenderPipeline { */ public TAAPassEffect: string = "TAAPassEffect"; - @serialize("samples") - private _samples = 8; /** * Number of accumulated samples (default: 16) */ + @serialize("samples") public set samples(samples: number) { - if (this._samples === samples) { - return; - } - - this._samples = samples; - this._hs.regenerate(samples); + this._taaThinPostProcess.samples = samples; } public get samples(): number { - return this._samples; + return this._taaThinPostProcess.samples; } @serialize("msaaSamples") @@ -74,14 +66,26 @@ export class TAARenderingPipeline extends PostProcessRenderPipeline { * The factor used to blend the history frame with current frame (default: 0.05) */ @serialize() - public factor = 0.05; + public get factor() { + return this._taaThinPostProcess.factor; + } + + public set factor(value: number) { + 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!) */ @serialize() - public disableOnCameraMove = true; + public get disableOnCameraMove() { + return this._taaThinPostProcess.disableOnCameraMove; + } + + public set disableOnCameraMove(value: boolean) { + this._taaThinPostProcess.disableOnCameraMove = value; + } @serialize("isEnabled") private _isEnabled = true; @@ -107,7 +111,7 @@ export class TAARenderingPipeline extends PostProcessRenderPipeline { } else if (value) { if (!this._isDirty) { if (this._cameras !== null) { - this._firstUpdate = true; + this._taaThinPostProcess._reset(); this._scene.postProcessRenderPipelineManager.attachCamerasToRenderPipeline(this._name, this._cameras); } } else { @@ -128,12 +132,11 @@ export class TAARenderingPipeline extends PostProcessRenderPipeline { private _camerasToBeAttached: Array = []; private _textureType: number; private _taaPostProcess: Nullable; + private _taaThinPostProcess: ThinTAAPostProcess; private _passPostProcess: Nullable; private _ping: RenderTargetWrapper; private _pong: RenderTargetWrapper; private _pingpong = 0; - private _hs: Halton2DSequence; - private _firstUpdate = true; /** * Returns true if TAA is supported by the running hardware @@ -162,7 +165,7 @@ export class TAARenderingPipeline extends PostProcessRenderPipeline { this._scene = scene; this._textureType = textureType; - this._hs = new Halton2DSequence(this.samples); + this._taaThinPostProcess = new ThinTAAPostProcess("TAA", this._scene.getEngine()); if (this.isSupported) { this._createPingPongTextures(engine.getRenderWidth(), engine.getRenderHeight()); @@ -230,15 +233,8 @@ export class TAARenderingPipeline extends PostProcessRenderPipeline { { generateMipMaps: false, generateDepthBuffer: false, type: Constants.TEXTURETYPE_HALF_FLOAT, samplingMode: Constants.TEXTURE_NEAREST_NEAREST } ); - this._hs.setDimensions(width / 2, height / 2); - this._hs.next(); - this._firstUpdate = true; - } - - private _updateEffectDefines(): void { - const defines: string[] = []; - - this._taaPostProcess?.updateEffect(defines.join("\n")); + this._taaThinPostProcess.textureWidth = width; + this._taaThinPostProcess.textureHeight = height; } private _buildPipeline() { @@ -313,45 +309,29 @@ export class TAARenderingPipeline extends PostProcessRenderPipeline { size: 1.0, engine: this._scene.getEngine(), textureType: this._textureType, + effectWrapper: this._taaThinPostProcess, }); this._taaPostProcess.samples = this._msaaSamples; - this._updateEffectDefines(); - this._taaPostProcess.onActivateObservable.add(() => { - const camera = this._scene.activeCamera; + 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 (camera && !camera.hasMoved) { - if (camera.mode === Camera.PERSPECTIVE_CAMERA) { - const projMat = camera.getProjectionMatrix(); - projMat.setRowFromFloats(2, this._hs.x, this._hs.y, projMat.m[10], projMat.m[11]); - } else { - // We must force the update of the projection matrix so that m[12] and m[13] are recomputed, as we modified them the previous frame - const projMat = camera.getProjectionMatrix(true); - projMat.setRowFromFloats(3, this._hs.x + projMat.m[12], this._hs.y + projMat.m[13], projMat.m[14], projMat.m[15]); - } - } + this._taaThinPostProcess.updateProjectionMatrix(); if (this._passPostProcess) { this._passPostProcess.inputTexture = this._pingpong ? this._ping : this._pong; } this._pingpong = this._pingpong ^ 1; - this._hs.next(); }); this._taaPostProcess.onApplyObservable.add((effect: Effect) => { - const camera = this._scene.activeCamera; - effect._bindTexture("historySampler", this._pingpong ? this._ping.texture : this._pong.texture); - effect.setFloat("factor", (camera?.hasMoved && this.disableOnCameraMove) || this._firstUpdate ? 1 : this.factor); - - this._firstUpdate = false; }); } diff --git a/packages/dev/core/src/PostProcesses/thinTAAPostProcess.ts b/packages/dev/core/src/PostProcesses/thinTAAPostProcess.ts new file mode 100644 index 00000000000..6c87c68c28a --- /dev/null +++ b/packages/dev/core/src/PostProcesses/thinTAAPostProcess.ts @@ -0,0 +1,181 @@ +// eslint-disable-next-line import/no-internal-modules +import type { Nullable, AbstractEngine, EffectWrapperCreationOptions } from "core/index"; +import { Camera } from "../Cameras/camera"; +import { Halton2DSequence } from "core/Maths/halton2DSequence"; +import { Engine } from "core/Engines/engine"; +import { EffectWrapper } from "core/Materials/effectRenderer"; + +/** + * Simple implementation of Temporal Anti-Aliasing (TAA). + * This can be used to improve image quality for still pictures (screenshots for e.g.). + */ +export class ThinTAAPostProcess extends EffectWrapper { + /** + * The fragment shader url + */ + public static readonly FragmentUrl = "taa"; + + /** + * The list of uniforms used by the effect + */ + public static readonly Uniforms = ["factor"]; + + /** + * The list of samplers used by the effect + */ + public static readonly Samplers = ["historySampler"]; + + protected override _gatherImports(useWebGPU: boolean, list: Promise[]) { + if (useWebGPU) { + this._webGPUReady = true; + list.push(import("../ShadersWGSL/taa.fragment")); + } else { + list.push(import("../Shaders/taa.fragment")); + } + } + + private _samples = 8; + /** + * Number of accumulated samples (default: 8) + */ + public set samples(samples: number) { + if (this._samples === samples) { + return; + } + + this._samples = samples; + this._hs.regenerate(samples); + } + + public get samples(): number { + return this._samples; + } + + /** + * The factor used to blend the history frame with current frame (default: 0.05) + */ + public factor = 0.05; + + /** + * The camera to use for the post process + */ + public camera: Nullable; + + private _disabled = false; + /** + * Whether the TAA is disabled + */ + public get disabled() { + return this._disabled; + } + + public set disabled(value: boolean) { + if (this._disabled === value) { + return; + } + this._disabled = value; + this._reset(); + } + + private _textureWidth = 0; + /** + * The width of the texture in which to render + */ + public get textureWidth() { + return this._textureWidth; + } + + public set textureWidth(width: number) { + if (this._textureWidth === width) { + return; + } + this._textureWidth = width; + this._reset(); + } + + private _textureHeight = 0; + /** + * The height of the texture in which to render + */ + public get textureHeight() { + return this._textureHeight; + } + + public set textureHeight(height: number) { + if (this._textureHeight === height) { + return; + } + this._textureHeight = height; + this._reset(); + } + + /** + * 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!) + */ + public disableOnCameraMove = true; + + private _hs: Halton2DSequence; + private _firstUpdate = true; + + /** + * Constructs a new TAA post process + * @param name Name of the effect + * @param engine Engine to use to render the effect. If not provided, the last created engine will be used + * @param options Options to configure the effect + */ + constructor(name: string, engine: Nullable = null, options?: EffectWrapperCreationOptions) { + super({ + ...options, + name, + engine: engine || Engine.LastCreatedEngine!, + useShaderStore: true, + useAsPostProcess: true, + fragmentShader: ThinTAAPostProcess.FragmentUrl, + uniforms: ThinTAAPostProcess.Uniforms, + samplers: ThinTAAPostProcess.Samplers, + }); + + this._hs = new Halton2DSequence(this.samples); + } + + /** @internal */ + public _reset(): void { + this._hs.setDimensions(this._textureWidth / 2, this._textureHeight / 2); + this._hs.next(); + this._firstUpdate = true; + } + + public updateProjectionMatrix(): void { + if (this.disabled) { + return; + } + + if (this.camera && !this.camera.hasMoved) { + if (this.camera.mode === Camera.PERSPECTIVE_CAMERA) { + const projMat = this.camera.getProjectionMatrix(); + projMat.setRowFromFloats(2, this._hs.x, this._hs.y, projMat.m[10], projMat.m[11]); + } else { + // We must force the update of the projection matrix so that m[12] and m[13] are recomputed, as we modified them the previous frame + const projMat = this.camera.getProjectionMatrix(true); + projMat.setRowFromFloats(3, this._hs.x + projMat.m[12], this._hs.y + projMat.m[13], projMat.m[14], projMat.m[15]); + } + } + + this._hs.next(); + } + + public override bind() { + super.bind(); + + if (this.disabled) { + return; + } + + const effect = this._drawWrapper.effect!; + + effect.setFloat("factor", (this.camera?.hasMoved && this.disableOnCameraMove) || this._firstUpdate ? 1 : this.factor); + + this._firstUpdate = false; + } +} diff --git a/packages/dev/core/src/ShadersWGSL/taa.fragment.fx b/packages/dev/core/src/ShadersWGSL/taa.fragment.fx new file mode 100644 index 00000000000..b0a7c93341f --- /dev/null +++ b/packages/dev/core/src/ShadersWGSL/taa.fragment.fx @@ -0,0 +1,11 @@ +var textureSampler: texture_2d; +var historySampler: texture_2d; + +uniform factor: f32; + +@fragment +fn main(input: FragmentInputs) -> FragmentOutputs { + let c = textureLoad(textureSampler, vec2(fragmentInputs.position.xy), 0); + let h = textureLoad(historySampler, vec2(fragmentInputs.position.xy), 0); + fragmentOutputs.color = mix(h, c, uniforms.factor); +} diff --git a/packages/tools/nodeRenderGraphEditor/src/blockTools.ts b/packages/tools/nodeRenderGraphEditor/src/blockTools.ts index ff49fed820b..b919f4a055d 100644 --- a/packages/tools/nodeRenderGraphEditor/src/blockTools.ts +++ b/packages/tools/nodeRenderGraphEditor/src/blockTools.ts @@ -18,6 +18,7 @@ import { NodeRenderGraphObjectRendererBlock } from "core/FrameGraph/Node/Blocks/ import { NodeRenderGraphGeometryRendererBlock } from "core/FrameGraph/Node/Blocks/Rendering/geometryRendererBlock"; import { NodeRenderGraphCullObjectsBlock } from "core/FrameGraph/Node/Blocks/Rendering/cullObjectsBlock"; import { NodeRenderGraphGUIBlock } from "gui/2D/FrameGraph/renderGraphGUIBlock"; +import { NodeRenderGraphTAAObjectRendererBlock } from "core/FrameGraph/Node/Blocks/Rendering/taaObjectRendererBlock"; import type { FrameGraph } from "core/FrameGraph/frameGraph"; /** @@ -84,6 +85,9 @@ export class BlockTools { case "GeometryRendererBlock": { return new NodeRenderGraphGeometryRendererBlock("Geometry renderer", frameGraph, scene); } + case "TAAObjectRendererBlock": { + return new NodeRenderGraphTAAObjectRendererBlock("TAA Object renderer", frameGraph, scene); + } case "CullBlock": { return new NodeRenderGraphCullObjectsBlock("Cull", frameGraph, scene); } diff --git a/packages/tools/nodeRenderGraphEditor/src/components/nodeList/nodeListComponent.tsx b/packages/tools/nodeRenderGraphEditor/src/components/nodeList/nodeListComponent.tsx index 1724a984d11..d76459337f9 100644 --- a/packages/tools/nodeRenderGraphEditor/src/components/nodeList/nodeListComponent.tsx +++ b/packages/tools/nodeRenderGraphEditor/src/components/nodeList/nodeListComponent.tsx @@ -41,6 +41,7 @@ export class NodeListComponent extends React.Component Date: Thu, 7 Nov 2024 18:09:26 +0100 Subject: [PATCH 2/3] Fix imports for UMD --- .../core/src/PostProcesses/RenderPipeline/Pipelines/index.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/dev/core/src/PostProcesses/RenderPipeline/Pipelines/index.ts b/packages/dev/core/src/PostProcesses/RenderPipeline/Pipelines/index.ts index 2f7318a269b..ab1ed8e86cd 100644 --- a/packages/dev/core/src/PostProcesses/RenderPipeline/Pipelines/index.ts +++ b/packages/dev/core/src/PostProcesses/RenderPipeline/Pipelines/index.ts @@ -19,3 +19,6 @@ export * from "../../../Shaders/screenSpaceReflection2BlurCombiner.fragment"; export * from "../../../ShadersWGSL/screenSpaceReflection2.fragment"; export * from "../../../ShadersWGSL/screenSpaceReflection2Blur.fragment"; export * from "../../../ShadersWGSL/screenSpaceReflection2BlurCombiner.fragment"; + +import "../../../Shaders/taa.fragment"; +import "../../../ShadersWGSL/taa.fragment"; From 8149391bdf2123adb8b6f46fd0a7c09b81536cd7 Mon Sep 17 00:00:00 2001 From: Popov72 Date: Thu, 7 Nov 2024 20:10:24 +0100 Subject: [PATCH 3/3] Fix UMD build --- .../src/FrameGraph/Tasks/Rendering/taaObjectRendererTask.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/dev/core/src/FrameGraph/Tasks/Rendering/taaObjectRendererTask.ts b/packages/dev/core/src/FrameGraph/Tasks/Rendering/taaObjectRendererTask.ts index 8d8af1b9c20..0cc2e007241 100644 --- a/packages/dev/core/src/FrameGraph/Tasks/Rendering/taaObjectRendererTask.ts +++ b/packages/dev/core/src/FrameGraph/Tasks/Rendering/taaObjectRendererTask.ts @@ -140,7 +140,5 @@ export class FrameGraphTAAObjectRendererTask extends FrameGraphObjectRendererTas passDisabled.useTexture(handle); } } - - return pass; } }