diff --git a/extras/render-passes/render-pass-bloom.js b/extras/render-passes/render-pass-bloom.js index 61a83968173..976d2192baf 100644 --- a/extras/render-passes/render-pass-bloom.js +++ b/extras/render-passes/render-pass-bloom.js @@ -24,7 +24,7 @@ class RenderPassBloom extends RenderPass { constructor(device, sourceTexture, format) { super(device); - this.sourceTexture = sourceTexture; + this._sourceTexture = sourceTexture; this.textureFormat = format; this.bloomRenderTarget = this.createRenderTarget(0); @@ -87,7 +87,7 @@ class RenderPassBloom extends RenderPass { const device = this.device; // progressive downscale - let passSourceTexture = this.sourceTexture; + let passSourceTexture = this._sourceTexture; for (let i = 0; i < numPasses; i++) { const pass = new RenderPassDownsample(device, passSourceTexture); @@ -124,11 +124,29 @@ class RenderPassBloom extends RenderPass { this.destroyRenderTargets(1); } + set sourceTexture(value) { + this._sourceTexture = value; + + if (this.beforePasses.length > 0) { + const firstPass = this.beforePasses[0]; + + // change resize source + firstPass.options.resizeSource = value; + + // change downsample source + firstPass.sourceTexture = value; + } + } + + get sourceTexture() { + return this._sourceTexture; + } + frameUpdate() { super.frameUpdate(); // create an appropriate amount of render passes - let numPasses = this.calcMipLevels(this.sourceTexture.width, this.sourceTexture.height, 2 ** this.lastMipLevel); + let numPasses = this.calcMipLevels(this._sourceTexture.width, this._sourceTexture.height, 2 ** this.lastMipLevel); numPasses = Math.max(1, numPasses); if (this.renderTargets.length !== numPasses) { diff --git a/extras/render-passes/render-pass-camera-frame.js b/extras/render-passes/render-pass-camera-frame.js index e88dc52b8fc..13d5fa152c6 100644 --- a/extras/render-passes/render-pass-camera-frame.js +++ b/extras/render-passes/render-pass-camera-frame.js @@ -23,6 +23,8 @@ class RenderPassCameraFrame extends RenderPass { bloomPass; + taaPass; + _bloomEnabled = true; _renderTargetScale = 1; @@ -174,11 +176,10 @@ class RenderPassCameraFrame extends RenderPass { // ------ TAA ------ - let taaPass; let sceneTextureWithTaa = sceneTexture; if (options.taaEnabled) { - taaPass = new RenderPassTAA(device, sceneTexture); - sceneTextureWithTaa = taaPass.accumulationTexture; + this.taaPass = new RenderPassTAA(device, sceneTexture); + sceneTextureWithTaa = this.taaPass.accumulationTexture; } // ------ BLOOM GENERATION ------ @@ -190,7 +191,7 @@ class RenderPassCameraFrame extends RenderPass { // create a compose pass, which combines the scene texture with the bloom texture this.composePass = new RenderPassCompose(app.graphicsDevice); - this.composePass.sceneTexture = sceneTextureWithTaa; + // this.composePass.sceneTexture = sceneTextureWithTaa; this.composePass.bloomTexture = this.bloomPass.bloomTexture; // compose pass renders directly to target renderTarget @@ -206,9 +207,23 @@ class RenderPassCameraFrame extends RenderPass { afterPass.addLayers(composition, cameraComponent, lastAddedIndex, clearRenderTarget); // use these prepared render passes in the order they should be executed - const allPasses = [this.scenePass, colorGrabPass, scenePassTransparent, taaPass, this.bloomPass, this.composePass, afterPass]; + const allPasses = [this.scenePass, colorGrabPass, scenePassTransparent, this.taaPass, this.bloomPass, this.composePass, afterPass]; this.beforePasses = allPasses.filter(element => element !== undefined); } + + frameUpdate() { + + super.frameUpdate(); + + // scene texture is either output of taa pass or the scene render target + const sceneTexture = this.taaPass?.update() ?? this._rt.colorBuffer; + + // TAA accumulation buffer is double buffered, assign the current one to the follow up passes. + this.composePass.sceneTexture = sceneTexture; + if (this.bloomEnabled) { + this.bloomPass.sourceTexture = sceneTexture; + } + } } export { RenderPassCameraFrame }; diff --git a/extras/render-passes/render-pass-taa.js b/extras/render-passes/render-pass-taa.js index 34d6b53ecbb..92cb52e17e1 100644 --- a/extras/render-passes/render-pass-taa.js +++ b/extras/render-passes/render-pass-taa.js @@ -1,8 +1,5 @@ import { FILTER_LINEAR, ADDRESS_CLAMP_TO_EDGE, - BLENDEQUATION_ADD, BLENDMODE_CONSTANT, BLENDMODE_ONE_MINUS_CONSTANT, - Color, - BlendState, RenderPassShaderQuad, Texture, RenderTarget @@ -10,9 +7,23 @@ import { class RenderPassTAA extends RenderPassShaderQuad { /** - * @type {Texture} + * The index of the accumulation texture to render to. + * + * @type {number} */ - accumulationTexture; + accumulationIndex = 0; + + accumulationTexture = null; + + /** + * @type {Texture[]} + */ + accumulationTextures = []; + + /** + * @type {RenderTarget[]} + */ + accumulationRenderTargets = []; constructor(device, sourceTexture) { super(device); @@ -21,18 +32,19 @@ class RenderPassTAA extends RenderPassShaderQuad { this.shader = this.createQuadShader('TaaResolveShader', ` uniform sampler2D sourceTexture; + uniform sampler2D accumulationTexture; varying vec2 uv0; void main() { vec4 src = texture2D(sourceTexture, uv0); - gl_FragColor = src; + vec4 acc = texture2D(accumulationTexture, uv0); + gl_FragColor = mix(acc, src, 0.05); }` ); this.sourceTextureId = device.scope.resolve('sourceTexture'); - - this.blendState = new BlendState(true, BLENDEQUATION_ADD, BLENDMODE_CONSTANT, BLENDMODE_ONE_MINUS_CONSTANT); + this.accumulationTextureId = device.scope.resolve('accumulationTexture'); this.setup(); } @@ -47,46 +59,48 @@ class RenderPassTAA extends RenderPassShaderQuad { setup() { - const { device } = this; - - // create the accumulation render target - const texture = new Texture(device, { - name: 'TAA Accumulation Texture', - width: 4, - height: 4, - format: this.sourceTexture.format, - mipmaps: false, - minFilter: FILTER_LINEAR, - magFilter: FILTER_LINEAR, - addressU: ADDRESS_CLAMP_TO_EDGE, - addressV: ADDRESS_CLAMP_TO_EDGE - }); - this.accumulationTexture = texture; - - const rt = new RenderTarget({ - colorBuffer: texture, - depth: false - }); + // double buffered accumulation render target + for (let i = 0; i < 2; ++i) { + this.accumulationTextures[i] = new Texture(this.device, { + name: `TAA-Accumulation-${i}`, + width: 4, + height: 4, + format: this.sourceTexture.format, + mipmaps: false, + minFilter: FILTER_LINEAR, + magFilter: FILTER_LINEAR, + addressU: ADDRESS_CLAMP_TO_EDGE, + addressV: ADDRESS_CLAMP_TO_EDGE + }); + + this.accumulationRenderTargets[i] = new RenderTarget({ + colorBuffer: this.accumulationTextures[i], + depth: false + }); + } - this.init(rt, { + this.accumulationTexture = this.accumulationTextures[0]; + this.init(this.accumulationRenderTargets[0], { resizeSource: this.sourceTexture }); - - // clear it to black initially - this.setClearColor(Color.BLACK); } execute() { this.sourceTextureId.setValue(this.sourceTexture); - - // TODO: add this to the parent class - const blend = 0.05; - this.device.setBlendColor(blend, blend, blend, blend); + this.accumulationTextureId.setValue(this.accumulationTextures[1 - this.accumulationIndex]); super.execute(); + } + + // called when the parent render pass gets added to the frame graph + update() { + + // swap source and destination accumulation texture + this.accumulationIndex = 1 - this.accumulationIndex; + this.accumulationTexture = this.accumulationTextures[this.accumulationIndex]; + this.renderTarget = this.accumulationRenderTargets[this.accumulationIndex]; - // disable clearing - this.setClearColor(); + return this.accumulationTexture; } }