123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363 |
- import {
- Clock,
- HalfFloatType,
- NoBlending,
- Vector2,
- WebGLRenderTarget
- } from 'three';
- import { CopyShader } from '../shaders/CopyShader.js';
- import { ShaderPass } from './ShaderPass.js';
- import { ClearMaskPass, MaskPass } from './MaskPass.js';
- /**
- * Used to implement post-processing effects in three.js.
- * The class manages a chain of post-processing passes to produce the final visual result.
- * Post-processing passes are executed in order of their addition/insertion.
- * The last pass is automatically rendered to screen.
- *
- * This module can only be used with {@link WebGLRenderer}.
- *
- * ```js
- * const composer = new EffectComposer( renderer );
- *
- * // adding some passes
- * const renderPass = new RenderPass( scene, camera );
- * composer.addPass( renderPass );
- *
- * const glitchPass = new GlitchPass();
- * composer.addPass( glitchPass );
- *
- * const outputPass = new OutputPass()
- * composer.addPass( outputPass );
- *
- * function animate() {
- *
- * composer.render(); // instead of renderer.render()
- *
- * }
- * ```
- *
- * @three_import import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
- */
- class EffectComposer {
- /**
- * Constructs a new effect composer.
- *
- * @param {WebGLRenderer} renderer - The renderer.
- * @param {WebGLRenderTarget} [renderTarget] - This render target and a clone will
- * be used as the internal read and write buffers. If not given, the composer creates
- * the buffers automatically.
- */
- constructor( renderer, renderTarget ) {
- /**
- * The renderer.
- *
- * @type {WebGLRenderer}
- */
- this.renderer = renderer;
- this._pixelRatio = renderer.getPixelRatio();
- if ( renderTarget === undefined ) {
- const size = renderer.getSize( new Vector2() );
- this._width = size.width;
- this._height = size.height;
- renderTarget = new WebGLRenderTarget( this._width * this._pixelRatio, this._height * this._pixelRatio, { type: HalfFloatType } );
- renderTarget.texture.name = 'EffectComposer.rt1';
- } else {
- this._width = renderTarget.width;
- this._height = renderTarget.height;
- }
- this.renderTarget1 = renderTarget;
- this.renderTarget2 = renderTarget.clone();
- this.renderTarget2.texture.name = 'EffectComposer.rt2';
- /**
- * A reference to the internal write buffer. Passes usually write
- * their result into this buffer.
- *
- * @type {WebGLRenderTarget}
- */
- this.writeBuffer = this.renderTarget1;
- /**
- * A reference to the internal read buffer. Passes usually read
- * the previous render result from this buffer.
- *
- * @type {WebGLRenderTarget}
- */
- this.readBuffer = this.renderTarget2;
- /**
- * Whether the final pass is rendered to the screen (default framebuffer) or not.
- *
- * @type {boolean}
- * @default true
- */
- this.renderToScreen = true;
- /**
- * An array representing the (ordered) chain of post-processing passes.
- *
- * @type {Array<Pass>}
- */
- this.passes = [];
- /**
- * A copy pass used for internal swap operations.
- *
- * @private
- * @type {ShaderPass}
- */
- this.copyPass = new ShaderPass( CopyShader );
- this.copyPass.material.blending = NoBlending;
- /**
- * The internal clock for managing time data.
- *
- * @private
- * @type {Clock}
- */
- this.clock = new Clock();
- }
- /**
- * Swaps the internal read/write buffers.
- */
- swapBuffers() {
- const tmp = this.readBuffer;
- this.readBuffer = this.writeBuffer;
- this.writeBuffer = tmp;
- }
- /**
- * Adds the given pass to the pass chain.
- *
- * @param {Pass} pass - The pass to add.
- */
- addPass( pass ) {
- this.passes.push( pass );
- pass.setSize( this._width * this._pixelRatio, this._height * this._pixelRatio );
- }
- /**
- * Inserts the given pass at a given index.
- *
- * @param {Pass} pass - The pass to insert.
- * @param {number} index - The index into the pass chain.
- */
- insertPass( pass, index ) {
- this.passes.splice( index, 0, pass );
- pass.setSize( this._width * this._pixelRatio, this._height * this._pixelRatio );
- }
- /**
- * Removes the given pass from the pass chain.
- *
- * @param {Pass} pass - The pass to remove.
- */
- removePass( pass ) {
- const index = this.passes.indexOf( pass );
- if ( index !== - 1 ) {
- this.passes.splice( index, 1 );
- }
- }
- /**
- * Returns `true` if the pass for the given index is the last enabled pass in the pass chain.
- *
- * @param {number} passIndex - The pass index.
- * @return {boolean} Whether the pass for the given index is the last pass in the pass chain.
- */
- isLastEnabledPass( passIndex ) {
- for ( let i = passIndex + 1; i < this.passes.length; i ++ ) {
- if ( this.passes[ i ].enabled ) {
- return false;
- }
- }
- return true;
- }
- /**
- * Executes all enabled post-processing passes in order to produce the final frame.
- *
- * @param {number} deltaTime - The delta time in seconds. If not given, the composer computes
- * its own time delta value.
- */
- render( deltaTime ) {
- // deltaTime value is in seconds
- if ( deltaTime === undefined ) {
- deltaTime = this.clock.getDelta();
- }
- const currentRenderTarget = this.renderer.getRenderTarget();
- let maskActive = false;
- for ( let i = 0, il = this.passes.length; 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 );
- }
- /**
- * Resets the internal state of the EffectComposer.
- *
- * @param {WebGLRenderTarget} [renderTarget] - This render target has the same purpose like
- * the one from the constructor. If set, it is used to setup the read and write buffers.
- */
- reset( renderTarget ) {
- 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;
- }
- /**
- * Resizes the internal read and write buffers as well as all passes. Similar to {@link WebGLRenderer#setSize},
- * this method honors the current pixel ration.
- *
- * @param {number} width - The width in logical pixels.
- * @param {number} height - The height in logical pixels.
- */
- setSize( width, height ) {
- 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 );
- }
- }
- /**
- * Sets device pixel ratio. This is usually used for HiDPI device to prevent blurring output.
- * Setting the pixel ratio will automatically resize the composer.
- *
- * @param {number} pixelRatio - The pixel ratio to set.
- */
- setPixelRatio( pixelRatio ) {
- this._pixelRatio = pixelRatio;
- this.setSize( this._width, this._height );
- }
- /**
- * Frees the GPU-related resources allocated by this instance. Call this
- * method whenever the composer is no longer used in your app.
- */
- dispose() {
- this.renderTarget1.dispose();
- this.renderTarget2.dispose();
- this.copyPass.dispose();
- }
- }
- export { EffectComposer };
|