Home Reference Source

src/passes/MaskPass.js

import { ClearPass } from "./ClearPass.js";
import { Pass } from "./Pass.js";

/**
 * A stencil mask pass.
 *
 * This pass requires that the input and output buffers have a stencil buffer. You can enable the stencil buffer via the
 * {@link EffectComposer} constructor.
 */

export class MaskPass extends Pass {

	/**
	 * Constructs a new mask pass.
	 *
	 * @param {Scene} scene - The scene to render.
	 * @param {Camera} camera - The camera to use.
	 */

	constructor(scene, camera) {

		super("MaskPass", scene, camera);

		this.needsSwap = false;

		/**
		 * A clear pass.
		 *
		 * @type {ClearPass}
		 * @readonly
		 */

		this.clearPass = new ClearPass(false, false, true);

		/**
		 * Inverse flag.
		 *
		 * @type {Boolean}
		 * @deprecated Use inverted instead.
		 */

		this.inverse = false;

	}

	set mainScene(value) {

		this.scene = value;

	}

	set mainCamera(value) {

		this.camera = value;

	}

	/**
	 * Indicates whether the mask should be inverted.
	 *
	 * @type {Boolean}
	 */

	get inverted() {

		return this.inverse;

	}

	set inverted(value) {

		this.inverse = value;

	}

	/**
	 * Indicates whether this pass should clear the stencil buffer.
	 *
	 * @type {Boolean}
	 * @deprecated Use clearPass.enabled instead.
	 */

	get clear() {

		return this.clearPass.enabled;

	}

	set clear(value) {

		this.clearPass.enabled = value;

	}

	/**
	 * Returns the internal clear pass.
	 *
	 * @deprecated Use clearPass.enabled instead.
	 * @return {ClearPass} The clear pass.
	 */

	getClearPass() {

		return this.clearPass;

	}

	/**
	 * Indicates whether the mask is inverted.
	 *
	 * @deprecated Use inverted instead.
	 * @return {Boolean} Whether the mask is inverted.
	 */

	isInverted() {

		return this.inverted;

	}

	/**
	 * Enables or disable mask inversion.
	 *
	 * @deprecated Use inverted instead.
	 * @param {Boolean} value - Whether the mask should be inverted.
	 */

	setInverted(value) {

		this.inverted = value;

	}

	/**
	 * Renders the effect.
	 *
	 * @param {WebGLRenderer} renderer - The renderer.
	 * @param {WebGLRenderTarget} inputBuffer - A frame buffer that contains the result of the previous pass.
	 * @param {WebGLRenderTarget} outputBuffer - A frame buffer that serves as the output render target unless this pass renders to screen.
	 * @param {Number} [deltaTime] - The time between the last frame and the current one in seconds.
	 * @param {Boolean} [stencilTest] - Indicates whether a stencil mask is active.
	 */

	render(renderer, inputBuffer, outputBuffer, deltaTime, stencilTest) {

		const context = renderer.getContext();
		const buffers = renderer.state.buffers;

		const scene = this.scene;
		const camera = this.camera;
		const clearPass = this.clearPass;

		const writeValue = this.inverted ? 0 : 1;
		const clearValue = 1 - writeValue;

		// Don't update color or depth.
		buffers.color.setMask(false);
		buffers.depth.setMask(false);

		// Lock the buffers.
		buffers.color.setLocked(true);
		buffers.depth.setLocked(true);

		// Configure the stencil.
		buffers.stencil.setTest(true);
		buffers.stencil.setOp(context.REPLACE, context.REPLACE, context.REPLACE);
		buffers.stencil.setFunc(context.ALWAYS, writeValue, 0xffffffff);
		buffers.stencil.setClear(clearValue);
		buffers.stencil.setLocked(true);

		// Clear the stencil.
		if(this.clearPass.enabled) {

			if(this.renderToScreen) {

				clearPass.render(renderer, null);

			} else {

				clearPass.render(renderer, inputBuffer);
				clearPass.render(renderer, outputBuffer);

			}

		}

		// Draw the mask.
		if(this.renderToScreen) {

			renderer.setRenderTarget(null);
			renderer.render(scene, camera);

		} else {

			renderer.setRenderTarget(inputBuffer);
			renderer.render(scene, camera);
			renderer.setRenderTarget(outputBuffer);
			renderer.render(scene, camera);

		}

		// Unlock the buffers.
		buffers.color.setLocked(false);
		buffers.depth.setLocked(false);

		// Only render where the stencil is set to 1.
		buffers.stencil.setLocked(false);
		buffers.stencil.setFunc(context.EQUAL, 1, 0xffffffff);
		buffers.stencil.setOp(context.KEEP, context.KEEP, context.KEEP);
		buffers.stencil.setLocked(true);

	}

}