From e0cc26c553102be4f9bb8f6bf52e263603d205aa Mon Sep 17 00:00:00 2001 From: Paras Sanghavi Date: Wed, 17 Mar 2021 01:55:51 -0700 Subject: [PATCH] chore: Move EffectsComposer into TS (#32) * Move FullScreenQuad into class * Move ShaderPass into TS * Convert Pass and ShaderPass to class defs * Move EffectsComposer into TS * better typing * Move EffectComposer to class * consts and lets instead of var * Cleanup * Public/private accessors * return types * Move mask function to class --- src/postprocessing/AdaptiveToneMappingPass.js | 4 +- src/postprocessing/AfterimagePass.js | 6 +- src/postprocessing/BloomPass.js | 4 +- src/postprocessing/BokehPass.js | 4 +- src/postprocessing/DotScreenPass.js | 4 +- src/postprocessing/EffectComposer.js | 194 ------------------ src/postprocessing/EffectComposer.ts | 194 ++++++++++++++++++ src/postprocessing/FilmPass.js | 4 +- src/postprocessing/GlitchPass.js | 4 +- src/postprocessing/HalftonePass.js | 4 +- .../{MaskPass.js => MaskPass.ts} | 58 +++--- src/postprocessing/OutlinePass.js | 4 +- src/postprocessing/Pass.js | 62 ------ src/postprocessing/Pass.ts | 63 ++++++ src/postprocessing/SAOPass.js | 7 +- src/postprocessing/SMAAPass.js | 8 +- src/postprocessing/SSAARenderPass.js | 4 +- src/postprocessing/SSAOPass.js | 4 +- src/postprocessing/SSRPass.js | 8 +- src/postprocessing/SavePass.js | 4 +- src/postprocessing/ShaderPass.js | 49 ----- src/postprocessing/ShaderPass.ts | 56 +++++ src/postprocessing/TexturePass.js | 4 +- src/postprocessing/UnrealBloomPass.js | 4 +- src/shaders/{CopyShader.js => CopyShader.ts} | 2 +- 25 files changed, 383 insertions(+), 376 deletions(-) delete mode 100644 src/postprocessing/EffectComposer.js create mode 100644 src/postprocessing/EffectComposer.ts rename src/postprocessing/{MaskPass.js => MaskPass.ts} (63%) delete mode 100644 src/postprocessing/Pass.js create mode 100644 src/postprocessing/Pass.ts delete mode 100644 src/postprocessing/ShaderPass.js create mode 100644 src/postprocessing/ShaderPass.ts rename src/shaders/{CopyShader.js => CopyShader.ts} (96%) diff --git a/src/postprocessing/AdaptiveToneMappingPass.js b/src/postprocessing/AdaptiveToneMappingPass.js index 57bf1778..ee369421 100644 --- a/src/postprocessing/AdaptiveToneMappingPass.js +++ b/src/postprocessing/AdaptiveToneMappingPass.js @@ -8,7 +8,7 @@ import { UniformsUtils, WebGLRenderTarget, } from 'three' -import { Pass } from '../postprocessing/Pass' +import { Pass, FullScreenQuad } from '../postprocessing/Pass' import { CopyShader } from '../shaders/CopyShader' import { LuminosityShader } from '../shaders/LuminosityShader' import { ToneMapShader } from '../shaders/ToneMapShader' @@ -122,7 +122,7 @@ var AdaptiveToneMappingPass = function (adaptive, resolution) { blending: NoBlending, }) - this.fsQuad = new Pass.FullScreenQuad(null) + this.fsQuad = new FullScreenQuad(null) } AdaptiveToneMappingPass.prototype = Object.assign(Object.create(Pass.prototype), { diff --git a/src/postprocessing/AfterimagePass.js b/src/postprocessing/AfterimagePass.js index 7fb747e5..33561f6a 100644 --- a/src/postprocessing/AfterimagePass.js +++ b/src/postprocessing/AfterimagePass.js @@ -7,7 +7,7 @@ import { UniformsUtils, WebGLRenderTarget, } from 'three' -import { Pass } from '../postprocessing/Pass' +import { Pass, FullScreenQuad } from '../postprocessing/Pass' import { AfterimageShader } from '../shaders/AfterimageShader' var AfterimagePass = function (damp) { @@ -39,10 +39,10 @@ var AfterimagePass = function (damp) { fragmentShader: this.shader.fragmentShader, }) - this.compFsQuad = new Pass.FullScreenQuad(this.shaderMaterial) + this.compFsQuad = new FullScreenQuad(this.shaderMaterial) var material = new MeshBasicMaterial() - this.copyFsQuad = new Pass.FullScreenQuad(material) + this.copyFsQuad = new FullScreenQuad(material) } AfterimagePass.prototype = Object.assign(Object.create(Pass.prototype), { diff --git a/src/postprocessing/BloomPass.js b/src/postprocessing/BloomPass.js index 2d1c766e..9018eb18 100644 --- a/src/postprocessing/BloomPass.js +++ b/src/postprocessing/BloomPass.js @@ -7,7 +7,7 @@ import { Vector2, WebGLRenderTarget, } from 'three' -import { Pass } from '../postprocessing/Pass' +import { Pass, FullScreenQuad } from '../postprocessing/Pass' import { CopyShader } from '../shaders/CopyShader' import { ConvolutionShader } from '../shaders/ConvolutionShader' @@ -73,7 +73,7 @@ var BloomPass = function (strength, kernelSize, sigma, resolution) { this.needsSwap = false - this.fsQuad = new Pass.FullScreenQuad(null) + this.fsQuad = new FullScreenQuad(null) } BloomPass.prototype = Object.assign(Object.create(Pass.prototype), { diff --git a/src/postprocessing/BokehPass.js b/src/postprocessing/BokehPass.js index 4aa8d0b5..236d806f 100644 --- a/src/postprocessing/BokehPass.js +++ b/src/postprocessing/BokehPass.js @@ -8,7 +8,7 @@ import { UniformsUtils, WebGLRenderTarget, } from 'three' -import { Pass } from '../postprocessing/Pass' +import { Pass, FullScreenQuad } from '../postprocessing/Pass' import { BokehShader } from '../shaders/BokehShader' /** @@ -72,7 +72,7 @@ var BokehPass = function (scene, camera, params) { this.uniforms = bokehUniforms this.needsSwap = false - this.fsQuad = new Pass.FullScreenQuad(this.materialBokeh) + this.fsQuad = new FullScreenQuad(this.materialBokeh) this._oldClearColor = new Color() } diff --git a/src/postprocessing/DotScreenPass.js b/src/postprocessing/DotScreenPass.js index a9cc97fa..9216d3a1 100644 --- a/src/postprocessing/DotScreenPass.js +++ b/src/postprocessing/DotScreenPass.js @@ -1,5 +1,5 @@ import { ShaderMaterial, UniformsUtils } from 'three' -import { Pass } from '../postprocessing/Pass' +import { Pass, FullScreenQuad } from '../postprocessing/Pass' import { DotScreenShader } from '../shaders/DotScreenShader' var DotScreenPass = function (center, angle, scale) { @@ -21,7 +21,7 @@ var DotScreenPass = function (center, angle, scale) { fragmentShader: shader.fragmentShader, }) - this.fsQuad = new Pass.FullScreenQuad(this.material) + this.fsQuad = new FullScreenQuad(this.material) } DotScreenPass.prototype = Object.assign(Object.create(Pass.prototype), { diff --git a/src/postprocessing/EffectComposer.js b/src/postprocessing/EffectComposer.js deleted file mode 100644 index 9c55c888..00000000 --- a/src/postprocessing/EffectComposer.js +++ /dev/null @@ -1,194 +0,0 @@ -import { - Clock, - LinearFilter, - Mesh, - OrthographicCamera, - PlaneGeometry, - RGBAFormat, - Vector2, - WebGLRenderTarget, -} from 'three' -import { CopyShader } from '../shaders/CopyShader' -import { ShaderPass } from '../postprocessing/ShaderPass' -import { MaskPass } from '../postprocessing/MaskPass' -import { ClearMaskPass } from '../postprocessing/MaskPass' - -var EffectComposer = function (renderer, renderTarget) { - this.renderer = renderer - - if (renderTarget === undefined) { - var parameters = { - minFilter: LinearFilter, - magFilter: LinearFilter, - format: RGBAFormat, - } - - var size = renderer.getSize(new Vector2()) - this._pixelRatio = renderer.getPixelRatio() - this._width = size.width - this._height = size.height - - renderTarget = new WebGLRenderTarget(this._width * this._pixelRatio, this._height * this._pixelRatio, parameters) - renderTarget.texture.name = 'EffectComposer.rt1' - } else { - this._pixelRatio = 1 - this._width = renderTarget.width - this._height = renderTarget.height - } - - this.renderTarget1 = renderTarget - this.renderTarget2 = renderTarget.clone() - this.renderTarget2.texture.name = 'EffectComposer.rt2' - - this.writeBuffer = this.renderTarget1 - this.readBuffer = this.renderTarget2 - - this.renderToScreen = true - - this.passes = [] - - // dependencies - - if (CopyShader === undefined) { - console.error('THREE.EffectComposer relies on CopyShader') - } - - if (ShaderPass === undefined) { - console.error('THREE.EffectComposer relies on ShaderPass') - } - - this.copyPass = new ShaderPass(CopyShader) - - this.clock = new Clock() -} - -Object.assign(EffectComposer.prototype, { - swapBuffers: function () { - var tmp = this.readBuffer - this.readBuffer = this.writeBuffer - this.writeBuffer = tmp - }, - - addPass: function (pass) { - this.passes.push(pass) - pass.setSize(this._width * this._pixelRatio, this._height * this._pixelRatio) - }, - - insertPass: function (pass, index) { - this.passes.splice(index, 0, pass) - pass.setSize(this._width * this._pixelRatio, this._height * this._pixelRatio) - }, - - removePass: function (pass) { - const index = this.passes.indexOf(pass) - - if (index !== -1) { - this.passes.splice(index, 1) - } - }, - - isLastEnabledPass: function (passIndex) { - for (let i = passIndex + 1; i < this.passes.length; i++) { - if (this.passes[i].enabled) { - return false - } - } - - return true - }, - - render: function (deltaTime) { - // deltaTime value is in seconds - - if (deltaTime === undefined) { - deltaTime = this.clock.getDelta() - } - - var currentRenderTarget = this.renderer.getRenderTarget() - - var maskActive = false - - var pass, - i, - il = this.passes.length - - for (i = 0; i < il; i++) { - pass = this.passes[i] - - if (pass.enabled === false) continue - - pass.renderToScreen = this.renderToScreen && this.isLastEnabledPass(i) - pass.render(this.renderer, this.writeBuffer, this.readBuffer, deltaTime, maskActive) - - if (pass.needsSwap) { - if (maskActive) { - var context = this.renderer.getContext() - var stencil = this.renderer.state.buffers.stencil - - //context.stencilFunc( context.NOTEQUAL, 1, 0xffffffff ); - stencil.setFunc(context.NOTEQUAL, 1, 0xffffffff) - - this.copyPass.render(this.renderer, this.writeBuffer, this.readBuffer, deltaTime) - - //context.stencilFunc( context.EQUAL, 1, 0xffffffff ); - stencil.setFunc(context.EQUAL, 1, 0xffffffff) - } - - this.swapBuffers() - } - - if (MaskPass !== undefined) { - if (pass instanceof MaskPass) { - maskActive = true - } else if (pass instanceof ClearMaskPass) { - maskActive = false - } - } - } - - this.renderer.setRenderTarget(currentRenderTarget) - }, - - reset: function (renderTarget) { - if (renderTarget === undefined) { - var size = this.renderer.getSize(new Vector2()) - this._pixelRatio = this.renderer.getPixelRatio() - this._width = size.width - this._height = size.height - - renderTarget = this.renderTarget1.clone() - renderTarget.setSize(this._width * this._pixelRatio, this._height * this._pixelRatio) - } - - this.renderTarget1.dispose() - this.renderTarget2.dispose() - this.renderTarget1 = renderTarget - this.renderTarget2 = renderTarget.clone() - - this.writeBuffer = this.renderTarget1 - this.readBuffer = this.renderTarget2 - }, - - setSize: function (width, height) { - this._width = width - this._height = height - - var effectiveWidth = this._width * this._pixelRatio - var effectiveHeight = this._height * this._pixelRatio - - this.renderTarget1.setSize(effectiveWidth, effectiveHeight) - this.renderTarget2.setSize(effectiveWidth, effectiveHeight) - - for (let i = 0; i < this.passes.length; i++) { - this.passes[i].setSize(effectiveWidth, effectiveHeight) - } - }, - - setPixelRatio: function (pixelRatio) { - this._pixelRatio = pixelRatio - - this.setSize(this._width, this._height) - }, -}) - -export { EffectComposer } diff --git a/src/postprocessing/EffectComposer.ts b/src/postprocessing/EffectComposer.ts new file mode 100644 index 00000000..d7915151 --- /dev/null +++ b/src/postprocessing/EffectComposer.ts @@ -0,0 +1,194 @@ +import { Clock, LinearFilter, RGBAFormat, Vector2, WebGLRenderer, WebGLRenderTarget } from 'three' +import { CopyShader } from '../shaders/CopyShader' +import { ShaderPass } from './ShaderPass' +import { MaskPass, ClearMaskPass } from './MaskPass' +import { Pass } from './Pass' + +class EffectComposer { + public renderer: WebGLRenderer + private _pixelRatio: number + private _width: number + private _height: number + public renderTarget1: WebGLRenderTarget + public renderTarget2: WebGLRenderTarget + public writeBuffer: WebGLRenderTarget + public readBuffer: WebGLRenderTarget + public renderToScreen: boolean + public passes: Pass[] = [] + public copyPass: Pass + public clock: Clock + + public constructor(renderer: WebGLRenderer, renderTarget: WebGLRenderTarget) { + this.renderer = renderer + + if (renderTarget === undefined) { + const parameters = { + minFilter: LinearFilter, + magFilter: LinearFilter, + format: RGBAFormat, + } + + const size = renderer.getSize(new Vector2()) + this._pixelRatio = renderer.getPixelRatio() + this._width = size.width + this._height = size.height + + renderTarget = new WebGLRenderTarget(this._width * this._pixelRatio, this._height * this._pixelRatio, parameters) + renderTarget.texture.name = 'EffectComposer.rt1' + } else { + this._pixelRatio = 1 + this._width = renderTarget.width + this._height = renderTarget.height + } + + this.renderTarget1 = renderTarget + this.renderTarget2 = renderTarget.clone() + this.renderTarget2.texture.name = 'EffectComposer.rt2' + + this.writeBuffer = this.renderTarget1 + this.readBuffer = this.renderTarget2 + + this.renderToScreen = true + + // dependencies + + if (CopyShader === undefined) { + console.error('THREE.EffectComposer relies on CopyShader') + } + + if (ShaderPass === undefined) { + console.error('THREE.EffectComposer relies on ShaderPass') + } + + this.copyPass = new ShaderPass(CopyShader) + + this.clock = new Clock() + } + + public swapBuffers(): void { + const tmp = this.readBuffer + this.readBuffer = this.writeBuffer + this.writeBuffer = tmp + } + + public addPass(pass: Pass): void { + this.passes.push(pass) + pass.setSize(this._width * this._pixelRatio, this._height * this._pixelRatio) + } + + public insertPass(pass: Pass, index: number): void { + this.passes.splice(index, 0, pass) + pass.setSize(this._width * this._pixelRatio, this._height * this._pixelRatio) + } + + public removePass(pass: Pass): void { + const index = this.passes.indexOf(pass) + + if (index !== -1) { + this.passes.splice(index, 1) + } + } + + public isLastEnabledPass(passIndex: number): boolean { + for (let i = passIndex + 1; i < this.passes.length; i++) { + if (this.passes[i].enabled) { + return false + } + } + + return true + } + + public render(deltaTime: number): void { + // deltaTime value is in seconds + + if (deltaTime === undefined) { + deltaTime = this.clock.getDelta() + } + + const currentRenderTarget = this.renderer.getRenderTarget() + + let maskActive = false + + const il = this.passes.length + + for (let i = 0; i < il; i++) { + const pass = this.passes[i] + + if (pass.enabled === false) continue + + pass.renderToScreen = this.renderToScreen && this.isLastEnabledPass(i) + pass.render(this.renderer, this.writeBuffer, this.readBuffer, deltaTime, maskActive) + + if (pass.needsSwap) { + if (maskActive) { + const context = this.renderer.getContext() + const stencil = this.renderer.state.buffers.stencil + + //context.stencilFunc( context.NOTEQUAL, 1, 0xffffffff ); + stencil.setFunc(context.NOTEQUAL, 1, 0xffffffff) + + this.copyPass.render(this.renderer, this.writeBuffer, this.readBuffer, deltaTime) + + //context.stencilFunc( context.EQUAL, 1, 0xffffffff ); + stencil.setFunc(context.EQUAL, 1, 0xffffffff) + } + + this.swapBuffers() + } + + if (MaskPass !== undefined) { + if (pass instanceof MaskPass) { + maskActive = true + } else if (pass instanceof ClearMaskPass) { + maskActive = false + } + } + } + + this.renderer.setRenderTarget(currentRenderTarget) + } + + public reset(renderTarget: WebGLRenderTarget): void { + if (renderTarget === undefined) { + const size = this.renderer.getSize(new Vector2()) + this._pixelRatio = this.renderer.getPixelRatio() + this._width = size.width + this._height = size.height + + renderTarget = this.renderTarget1.clone() + renderTarget.setSize(this._width * this._pixelRatio, this._height * this._pixelRatio) + } + + this.renderTarget1.dispose() + this.renderTarget2.dispose() + this.renderTarget1 = renderTarget + this.renderTarget2 = renderTarget.clone() + + this.writeBuffer = this.renderTarget1 + this.readBuffer = this.renderTarget2 + } + + public setSize(width: number, height: number): void { + this._width = width + this._height = height + + const effectiveWidth = this._width * this._pixelRatio + const effectiveHeight = this._height * this._pixelRatio + + this.renderTarget1.setSize(effectiveWidth, effectiveHeight) + this.renderTarget2.setSize(effectiveWidth, effectiveHeight) + + for (let i = 0; i < this.passes.length; i++) { + this.passes[i].setSize(effectiveWidth, effectiveHeight) + } + } + + public setPixelRatio(pixelRatio: number): void { + this._pixelRatio = pixelRatio + + this.setSize(this._width, this._height) + } +} + +export { EffectComposer } diff --git a/src/postprocessing/FilmPass.js b/src/postprocessing/FilmPass.js index 99dc1479..2e0091a4 100644 --- a/src/postprocessing/FilmPass.js +++ b/src/postprocessing/FilmPass.js @@ -1,5 +1,5 @@ import { ShaderMaterial, UniformsUtils } from 'three' -import { Pass } from '../postprocessing/Pass' +import { Pass, FullScreenQuad } from '../postprocessing/Pass' import { FilmShader } from '../shaders/FilmShader' var FilmPass = function (noiseIntensity, scanlinesIntensity, scanlinesCount, grayscale) { @@ -22,7 +22,7 @@ var FilmPass = function (noiseIntensity, scanlinesIntensity, scanlinesCount, gra if (scanlinesIntensity !== undefined) this.uniforms.sIntensity.value = scanlinesIntensity if (scanlinesCount !== undefined) this.uniforms.sCount.value = scanlinesCount - this.fsQuad = new Pass.FullScreenQuad(this.material) + this.fsQuad = new FullScreenQuad(this.material) } FilmPass.prototype = Object.assign(Object.create(Pass.prototype), { diff --git a/src/postprocessing/GlitchPass.js b/src/postprocessing/GlitchPass.js index b49b6e69..26cfa393 100644 --- a/src/postprocessing/GlitchPass.js +++ b/src/postprocessing/GlitchPass.js @@ -1,5 +1,5 @@ import { DataTexture, FloatType, MathUtils, RGBFormat, ShaderMaterial, UniformsUtils } from 'three' -import { Pass } from '../postprocessing/Pass' +import { Pass, FullScreenQuad } from '../postprocessing/Pass' import { DigitalGlitch } from '../shaders/DigitalGlitch' var GlitchPass = function (dt_size) { @@ -20,7 +20,7 @@ var GlitchPass = function (dt_size) { fragmentShader: shader.fragmentShader, }) - this.fsQuad = new Pass.FullScreenQuad(this.material) + this.fsQuad = new FullScreenQuad(this.material) this.goWild = false this.curF = 0 diff --git a/src/postprocessing/HalftonePass.js b/src/postprocessing/HalftonePass.js index e9f17fb0..5590352d 100644 --- a/src/postprocessing/HalftonePass.js +++ b/src/postprocessing/HalftonePass.js @@ -1,5 +1,5 @@ import { ShaderMaterial, UniformsUtils } from 'three' -import { Pass } from '../postprocessing/Pass' +import { Pass, FullScreenQuad } from '../postprocessing/Pass' import { HalftoneShader } from '../shaders/HalftoneShader' /** @@ -30,7 +30,7 @@ var HalftonePass = function (width, height, params) { } } - this.fsQuad = new Pass.FullScreenQuad(this.material) + this.fsQuad = new FullScreenQuad(this.material) } HalftonePass.prototype = Object.assign(Object.create(Pass.prototype), { diff --git a/src/postprocessing/MaskPass.js b/src/postprocessing/MaskPass.ts similarity index 63% rename from src/postprocessing/MaskPass.js rename to src/postprocessing/MaskPass.ts index 9dbc680a..f086dfbd 100644 --- a/src/postprocessing/MaskPass.js +++ b/src/postprocessing/MaskPass.ts @@ -1,23 +1,30 @@ -import { Pass } from '../postprocessing/Pass' +import { Camera, Scene, WebGLRenderer, WebGLRenderTarget } from 'three' +import { Pass } from './Pass' -var MaskPass = function (scene, camera) { - Pass.call(this) +class MaskPass extends Pass { + public scene: Scene + public camera: Camera + public inverse: boolean - this.scene = scene - this.camera = camera + public constructor(scene: Scene, camera: Camera) { + super() - this.clear = true - this.needsSwap = false + this.scene = scene + this.camera = camera - this.inverse = false -} + this.clear = true + this.needsSwap = false -MaskPass.prototype = Object.assign(Object.create(Pass.prototype), { - constructor: MaskPass, + this.inverse = false + } - render: function (renderer, writeBuffer, readBuffer /*, deltaTime, maskActive */) { - var context = renderer.getContext() - var state = renderer.state + public render( + renderer: WebGLRenderer, + writeBuffer: WebGLRenderTarget, + readBuffer: WebGLRenderTarget /*, deltaTime, maskActive */, + ): void { + const context = renderer.getContext() + const state = renderer.state // don't update color or depth @@ -31,7 +38,7 @@ MaskPass.prototype = Object.assign(Object.create(Pass.prototype), { // set up stencil - var writeValue, clearValue + let writeValue, clearValue if (this.inverse) { writeValue = 0 @@ -68,22 +75,19 @@ MaskPass.prototype = Object.assign(Object.create(Pass.prototype), { state.buffers.stencil.setFunc(context.EQUAL, 1, 0xffffffff) // draw if == 1 state.buffers.stencil.setOp(context.KEEP, context.KEEP, context.KEEP) state.buffers.stencil.setLocked(true) - }, -}) - -var ClearMaskPass = function () { - Pass.call(this) - - this.needsSwap = false + } } -ClearMaskPass.prototype = Object.create(Pass.prototype) +class ClearMaskPass extends Pass { + public constructor() { + super() + this.needsSwap = false + } -Object.assign(ClearMaskPass.prototype, { - render: function (renderer /*, writeBuffer, readBuffer, deltaTime, maskActive */) { + public render(renderer: WebGLRenderer /*, writeBuffer, readBuffer, deltaTime, maskActive */): void { renderer.state.buffers.stencil.setLocked(false) renderer.state.buffers.stencil.setTest(false) - }, -}) + } +} export { MaskPass, ClearMaskPass } diff --git a/src/postprocessing/OutlinePass.js b/src/postprocessing/OutlinePass.js index af7d8c37..578ba28c 100644 --- a/src/postprocessing/OutlinePass.js +++ b/src/postprocessing/OutlinePass.js @@ -15,7 +15,7 @@ import { Vector3, WebGLRenderTarget, } from 'three' -import { Pass } from '../postprocessing/Pass' +import { Pass, FullScreenQuad } from '../postprocessing/Pass' import { CopyShader } from '../shaders/CopyShader' var OutlinePass = function (resolution, scene, camera, selectedObjects) { @@ -124,7 +124,7 @@ var OutlinePass = function (resolution, scene, camera, selectedObjects) { this._oldClearColor = new Color() this.oldClearAlpha = 1 - this.fsQuad = new Pass.FullScreenQuad(null) + this.fsQuad = new FullScreenQuad(null) this.tempPulseColor1 = new Color() this.tempPulseColor2 = new Color() diff --git a/src/postprocessing/Pass.js b/src/postprocessing/Pass.js deleted file mode 100644 index 587b5093..00000000 --- a/src/postprocessing/Pass.js +++ /dev/null @@ -1,62 +0,0 @@ -import { OrthographicCamera, PlaneGeometry, Mesh } from 'three' - -function Pass() { - // if set to true, the pass is processed by the composer - this.enabled = true - - // if set to true, the pass indicates to swap read and write buffer after rendering - this.needsSwap = true - - // if set to true, the pass clears its buffer before rendering - this.clear = false - - // if set to true, the result of the pass is rendered to screen. This is set automatically by EffectComposer. - this.renderToScreen = false -} - -Object.assign(Pass.prototype, { - setSize: function (/* width, height */) {}, - - render: function (/* renderer, writeBuffer, readBuffer, deltaTime, maskActive */) { - console.error('THREE.Pass: .render() must be implemented in derived pass.') - }, -}) - -// Helper for passes that need to fill the viewport with a single quad. - -// Important: It's actually a hack to put FullScreenQuad into the Pass namespace. This is only -// done to make examples/js code work. Normally, FullScreenQuad should be exported -// from this module like Pass. - -Pass.FullScreenQuad = (function () { - var camera = new OrthographicCamera(-1, 1, 1, -1, 0, 1) - var geometry = new PlaneGeometry(2, 2) - - var FullScreenQuad = function (material) { - this._mesh = new Mesh(geometry, material) - } - - Object.defineProperty(FullScreenQuad.prototype, 'material', { - get: function () { - return this._mesh.material - }, - - set: function (value) { - this._mesh.material = value - }, - }) - - Object.assign(FullScreenQuad.prototype, { - dispose: function () { - this._mesh.geometry.dispose() - }, - - render: function (renderer) { - renderer.render(this._mesh, camera) - }, - }) - - return FullScreenQuad -})() - -export { Pass } diff --git a/src/postprocessing/Pass.ts b/src/postprocessing/Pass.ts new file mode 100644 index 00000000..13c95704 --- /dev/null +++ b/src/postprocessing/Pass.ts @@ -0,0 +1,63 @@ +import { OrthographicCamera, PlaneGeometry, Mesh, Material, Renderer, WebGLRenderer, WebGLRenderTarget } from 'three' + +class Pass { + // if set to true, the pass is processed by the composer + public enabled = true + + // if set to true, the pass indicates to swap read and write buffer after rendering + public needsSwap = true + + // if set to true, the pass clears its buffer before rendering + public clear = false + + // if set to true, the result of the pass is rendered to screen. This is set automatically by EffectComposer. + public renderToScreen = false + + public setSize( + /* eslint-disable @typescript-eslint/no-unused-vars */ + /* eslint-disable no-unused-vars */ + width: number, + height: number, + ): void {} + + public render( + /* eslint-disable @typescript-eslint/no-unused-vars */ + /* eslint-disable no-unused-vars */ + renderer: WebGLRenderer, + writeBuffer: WebGLRenderTarget, + readBuffer: WebGLRenderTarget, + deltaTime: number, + maskActive?: unknown, + ): void { + console.error('THREE.Pass: .render() must be implemented in derived pass.') + } +} + +// Helper for passes that need to fill the viewport with a single quad. +class FullScreenQuad { + public camera = new OrthographicCamera(-1, 1, 1, -1, 0, 1) + public geometry = new PlaneGeometry(2, 2) + private mesh: Mesh + + public constructor(material: TMaterial) { + this.mesh = new Mesh(this.geometry, material) + } + + public get material(): TMaterial { + return this.mesh.material + } + + public set material(value: TMaterial) { + this.mesh.material = value + } + + public dispose(): void { + this.mesh.geometry.dispose() + } + + public render(renderer: Renderer): void { + renderer.render(this.mesh, this.camera) + } +} + +export { Pass, FullScreenQuad } diff --git a/src/postprocessing/SAOPass.js b/src/postprocessing/SAOPass.js index a74aa9e7..4f88fc12 100644 --- a/src/postprocessing/SAOPass.js +++ b/src/postprocessing/SAOPass.js @@ -19,10 +19,9 @@ import { WebGLRenderTarget, ZeroFactor, } from 'three' -import { Pass } from '../postprocessing/Pass' +import { Pass, FullScreenQuad } from '../postprocessing/Pass' import { SAOShader } from '../shaders/SAOShader' -import { DepthLimitedBlurShader } from '../shaders/DepthLimitedBlurShader' -import { BlurShaderUtils } from '../shaders/DepthLimitedBlurShader' +import { DepthLimitedBlurShader, BlurShaderUtils } from '../shaders/DepthLimitedBlurShader' import { CopyShader } from '../shaders/CopyShader' import { UnpackDepthRGBAShader } from '../shaders/UnpackDepthRGBAShader' @@ -180,7 +179,7 @@ var SAOPass = function (scene, camera, depthTexture, useNormals, resolution) { blending: NoBlending, }) - this.fsQuad = new Pass.FullScreenQuad(null) + this.fsQuad = new FullScreenQuad(null) } SAOPass.OUTPUT = { diff --git a/src/postprocessing/SMAAPass.js b/src/postprocessing/SMAAPass.js index 651432f3..5c84f1c7 100644 --- a/src/postprocessing/SMAAPass.js +++ b/src/postprocessing/SMAAPass.js @@ -8,10 +8,8 @@ import { UniformsUtils, WebGLRenderTarget, } from 'three' -import { Pass } from '../postprocessing/Pass' -import { SMAAEdgesShader } from '../shaders/SMAAShader' -import { SMAAWeightsShader } from '../shaders/SMAAShader' -import { SMAABlendShader } from '../shaders/SMAAShader' +import { Pass, FullScreenQuad } from '../postprocessing/Pass' +import { SMAAEdgesShader, SMAAWeightsShader, SMAABlendShader } from '../shaders/SMAAShader' var SMAAPass = function (width, height) { Pass.call(this) @@ -115,7 +113,7 @@ var SMAAPass = function (width, height) { this.needsSwap = false - this.fsQuad = new Pass.FullScreenQuad(null) + this.fsQuad = new FullScreenQuad(null) } SMAAPass.prototype = Object.assign(Object.create(Pass.prototype), { diff --git a/src/postprocessing/SSAARenderPass.js b/src/postprocessing/SSAARenderPass.js index a630813a..c7567ab9 100644 --- a/src/postprocessing/SSAARenderPass.js +++ b/src/postprocessing/SSAARenderPass.js @@ -7,7 +7,7 @@ import { UniformsUtils, WebGLRenderTarget, } from 'three' -import { Pass } from '../postprocessing/Pass' +import { Pass, FullScreenQuad } from '../postprocessing/Pass' import { CopyShader } from '../shaders/CopyShader' /** @@ -50,7 +50,7 @@ var SSAARenderPass = function (scene, camera, clearColor, clearAlpha) { depthWrite: false, }) - this.fsQuad = new Pass.FullScreenQuad(this.copyMaterial) + this.fsQuad = new FullScreenQuad(this.copyMaterial) } SSAARenderPass.prototype = Object.assign(Object.create(Pass.prototype), { diff --git a/src/postprocessing/SSAOPass.js b/src/postprocessing/SSAOPass.js index 87430ba2..0da653ac 100644 --- a/src/postprocessing/SSAOPass.js +++ b/src/postprocessing/SSAOPass.js @@ -21,7 +21,7 @@ import { WebGLRenderTarget, ZeroFactor, } from 'three' -import { Pass } from '../postprocessing/Pass' +import { Pass, FullScreenQuad } from '../postprocessing/Pass' import { SimplexNoise } from '../math/SimplexNoise' import { SSAOBlurShader, SSAODepthShader, SSAOShader } from '../shaders/SSAOShader' import { CopyShader } from '../shaders/CopyShader' @@ -154,7 +154,7 @@ var SSAOPass = function (scene, camera, width, height) { blendEquationAlpha: AddEquation, }) - this.fsQuad = new Pass.FullScreenQuad(null) + this.fsQuad = new FullScreenQuad(null) this.originalClearColor = new Color() } diff --git a/src/postprocessing/SSRPass.js b/src/postprocessing/SSRPass.js index 81ebe212..b9bf4210 100644 --- a/src/postprocessing/SSRPass.js +++ b/src/postprocessing/SSRPass.js @@ -17,10 +17,8 @@ import { WebGLRenderTarget, HalfFloatType, } from 'three' -import { Pass } from '../postprocessing/Pass' -import { SSRShader } from '../shaders/SSRShader' -import { SSRBlurShader } from '../shaders/SSRShader' -import { SSRDepthShader } from '../shaders/SSRShader' +import { Pass, FullScreenQuad } from '../postprocessing/Pass' +import { SSRShader, SSRBlurShader, SSRDepthShader } from '../shaders/SSRShader' import { CopyShader } from '../shaders/CopyShader' var SSRPass = function ({ @@ -311,7 +309,7 @@ var SSRPass = function ({ // premultipliedAlpha:true, }) - this.fsQuad = new Pass.FullScreenQuad(null) + this.fsQuad = new FullScreenQuad(null) this.originalClearColor = new Color() } diff --git a/src/postprocessing/SavePass.js b/src/postprocessing/SavePass.js index 7cc6277d..f7507349 100644 --- a/src/postprocessing/SavePass.js +++ b/src/postprocessing/SavePass.js @@ -1,5 +1,5 @@ import { LinearFilter, RGBFormat, ShaderMaterial, UniformsUtils, WebGLRenderTarget } from 'three' -import { Pass } from '../postprocessing/Pass' +import { Pass, FullScreenQuad } from '../postprocessing/Pass' import { CopyShader } from '../shaders/CopyShader' var SavePass = function (renderTarget) { @@ -32,7 +32,7 @@ var SavePass = function (renderTarget) { this.needsSwap = false - this.fsQuad = new Pass.FullScreenQuad(this.material) + this.fsQuad = new FullScreenQuad(this.material) } SavePass.prototype = Object.assign(Object.create(Pass.prototype), { diff --git a/src/postprocessing/ShaderPass.js b/src/postprocessing/ShaderPass.js deleted file mode 100644 index dc32fffa..00000000 --- a/src/postprocessing/ShaderPass.js +++ /dev/null @@ -1,49 +0,0 @@ -import { ShaderMaterial, UniformsUtils } from 'three' -import { Pass } from '../postprocessing/Pass' - -var ShaderPass = function (shader, textureID) { - Pass.call(this) - - this.textureID = textureID !== undefined ? textureID : 'tDiffuse' - - if (shader instanceof ShaderMaterial) { - this.uniforms = shader.uniforms - - this.material = shader - } else if (shader) { - this.uniforms = UniformsUtils.clone(shader.uniforms) - - this.material = new ShaderMaterial({ - defines: Object.assign({}, shader.defines), - uniforms: this.uniforms, - vertexShader: shader.vertexShader, - fragmentShader: shader.fragmentShader, - }) - } - - this.fsQuad = new Pass.FullScreenQuad(this.material) -} - -ShaderPass.prototype = Object.assign(Object.create(Pass.prototype), { - constructor: ShaderPass, - - render: function (renderer, writeBuffer, readBuffer /*, deltaTime, maskActive */) { - if (this.uniforms[this.textureID]) { - this.uniforms[this.textureID].value = readBuffer.texture - } - - this.fsQuad.material = this.material - - if (this.renderToScreen) { - renderer.setRenderTarget(null) - this.fsQuad.render(renderer) - } else { - renderer.setRenderTarget(writeBuffer) - // TODO: Avoid using autoClear properties, see https://github.com/mrdoob/three.js/pull/15571#issuecomment-465669600 - if (this.clear) renderer.clear(renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil) - this.fsQuad.render(renderer) - } - }, -}) - -export { ShaderPass } diff --git a/src/postprocessing/ShaderPass.ts b/src/postprocessing/ShaderPass.ts new file mode 100644 index 00000000..61bc522e --- /dev/null +++ b/src/postprocessing/ShaderPass.ts @@ -0,0 +1,56 @@ +import { Shader, ShaderMaterial, UniformsUtils, WebGLRenderer, WebGLRenderTarget } from 'three' +import { Pass, FullScreenQuad } from './Pass' + +class ShaderPass extends Pass { + public textureID: string + public uniforms: Shader['uniforms'] + public material: ShaderMaterial + public fsQuad: FullScreenQuad + + public constructor(shader: ShaderMaterial | (Shader & { defines?: Object }), textureID = 'tDiffuse') { + super() + + this.textureID = textureID + + if (shader instanceof ShaderMaterial) { + this.uniforms = shader.uniforms + + this.material = shader + } else { + this.uniforms = UniformsUtils.clone(shader.uniforms) + + this.material = new ShaderMaterial({ + defines: Object.assign({}, shader.defines), + uniforms: this.uniforms, + vertexShader: shader.vertexShader, + fragmentShader: shader.fragmentShader, + }) + } + + this.fsQuad = new FullScreenQuad(this.material) + } + + public render( + renderer: WebGLRenderer, + writeBuffer: WebGLRenderTarget, + readBuffer: WebGLRenderTarget /*, deltaTime, maskActive */, + ): void { + if (this.uniforms[this.textureID]) { + this.uniforms[this.textureID].value = readBuffer.texture + } + + this.fsQuad.material = this.material + + if (this.renderToScreen) { + renderer.setRenderTarget(null) + this.fsQuad.render(renderer) + } else { + renderer.setRenderTarget(writeBuffer) + // TODO: Avoid using autoClear properties, see https://github.com/mrdoob/three.js/pull/15571#issuecomment-465669600 + if (this.clear) renderer.clear(renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil) + this.fsQuad.render(renderer) + } + } +} + +export { ShaderPass } diff --git a/src/postprocessing/TexturePass.js b/src/postprocessing/TexturePass.js index 7955c551..f1e8a490 100644 --- a/src/postprocessing/TexturePass.js +++ b/src/postprocessing/TexturePass.js @@ -1,5 +1,5 @@ import { ShaderMaterial, UniformsUtils } from 'three' -import { Pass } from '../postprocessing/Pass' +import { Pass, FullScreenQuad } from '../postprocessing/Pass' import { CopyShader } from '../shaders/CopyShader' var TexturePass = function (map, opacity) { @@ -24,7 +24,7 @@ var TexturePass = function (map, opacity) { this.needsSwap = false - this.fsQuad = new Pass.FullScreenQuad(null) + this.fsQuad = new FullScreenQuad(null) } TexturePass.prototype = Object.assign(Object.create(Pass.prototype), { diff --git a/src/postprocessing/UnrealBloomPass.js b/src/postprocessing/UnrealBloomPass.js index a378a833..7b7c9c8e 100644 --- a/src/postprocessing/UnrealBloomPass.js +++ b/src/postprocessing/UnrealBloomPass.js @@ -10,7 +10,7 @@ import { Vector3, WebGLRenderTarget, } from 'three' -import { Pass } from '../postprocessing/Pass' +import { Pass, FullScreenQuad } from '../postprocessing/Pass' import { CopyShader } from '../shaders/CopyShader' import { LuminosityHighPassShader } from '../shaders/LuminosityHighPassShader' @@ -153,7 +153,7 @@ var UnrealBloomPass = function (resolution, strength, radius, threshold) { this.basic = new MeshBasicMaterial() - this.fsQuad = new Pass.FullScreenQuad(null) + this.fsQuad = new FullScreenQuad(null) } UnrealBloomPass.prototype = Object.assign(Object.create(Pass.prototype), { diff --git a/src/shaders/CopyShader.js b/src/shaders/CopyShader.ts similarity index 96% rename from src/shaders/CopyShader.js rename to src/shaders/CopyShader.ts index 9a69a1e7..eb95cb76 100644 --- a/src/shaders/CopyShader.js +++ b/src/shaders/CopyShader.ts @@ -2,7 +2,7 @@ * Full-screen textured quad shader */ -var CopyShader = { +const CopyShader = { uniforms: { tDiffuse: { value: null }, opacity: { value: 1.0 },