Home Reference Source

src/materials/LuminanceMaterial.js

import { NoBlending, REVISION, ShaderMaterial, Uniform } from "three";

import fragmentShader from "./glsl/luminance.frag";
import vertexShader from "./glsl/common.vert";

/**
 * A luminance shader material.
 *
 * This shader produces a greyscale luminance map that describes the absolute amount of light emitted by a scene. It can
 * also be configured to output colors that are scaled with their respective luminance value. Additionally, a range may
 * be provided to mask out undesired texels.
 *
 * The alpha channel always contains the luminance value.
 *
 * On luminance coefficients:
 *  http://www.poynton.com/notes/colour_and_gamma/ColorFAQ.html#RTFToC9
 *
 * Coefficients for different color spaces:
 *  https://hsto.org/getpro/habr/post_images/2ab/69d/084/2ab69d084f9a597e032624bcd74d57a7.png
 *
 * Luminance range reference:
 *  https://cycling74.com/2007/05/23/your-first-shader/#.Vty9FfkrL4Z
 */

export class LuminanceMaterial extends ShaderMaterial {

	/**
	 * Constructs a new luminance material.
	 *
	 * @param {Boolean} [colorOutput=false] - Defines whether the shader should output colors scaled with their luminance value.
	 * @param {Vector2} [luminanceRange] - If provided, the shader will mask out texels that aren't in the specified luminance range.
	 */

	constructor(colorOutput = false, luminanceRange = null) {

		super({
			name: "LuminanceMaterial",
			defines: {
				THREE_REVISION: REVISION.replace(/\D+/g, "")
			},
			uniforms: {
				inputBuffer: new Uniform(null),
				threshold: new Uniform(0.0),
				smoothing: new Uniform(1.0),
				range: new Uniform(null)
			},
			blending: NoBlending,
			toneMapped: false,
			depthWrite: false,
			depthTest: false,
			fragmentShader,
			vertexShader
		});

		this.colorOutput = colorOutput;
		this.luminanceRange = luminanceRange;

	}

	/**
	 * The input buffer.
	 *
	 * @type {Texture}
	 */

	set inputBuffer(value) {

		this.uniforms.inputBuffer.value = value;

	}

	/**
	 * Sets the input buffer.
	 *
	 * @deprecated Use inputBuffer instead.
	 * @param {Texture} value - The input buffer.
	 */

	setInputBuffer(value) {

		this.uniforms.inputBuffer.value = value;

	}

	/**
	 * The luminance threshold.
	 *
	 * @type {Number}
	 */

	get threshold() {

		return this.uniforms.threshold.value;

	}

	set threshold(value) {

		if(this.smoothing > 0 || value > 0) {

			this.defines.THRESHOLD = "1";

		} else {

			delete this.defines.THRESHOLD;

		}

		this.uniforms.threshold.value = value;

	}

	/**
	 * Returns the luminance threshold.
	 *
	 * @deprecated Use threshold instead.
	 * @return {Number} The threshold.
	 */

	getThreshold() {

		return this.threshold;

	}

	/**
	 * Sets the luminance threshold.
	 *
	 * @deprecated Use threshold instead.
	 * @param {Number} value - The threshold.
	 */

	setThreshold(value) {

		this.threshold = value;

	}

	/**
	 * The luminance threshold smoothing.
	 *
	 * @type {Number}
	 */

	get smoothing() {

		return this.uniforms.smoothing.value;

	}

	set smoothing(value) {

		if(this.threshold > 0 || value > 0) {

			this.defines.THRESHOLD = "1";

		} else {

			delete this.defines.THRESHOLD;

		}

		this.uniforms.smoothing.value = value;

	}

	/**
	 * Returns the luminance threshold smoothing factor.
	 *
	 * @deprecated Use smoothing instead.
	 * @return {Number} The smoothing factor.
	 */

	getSmoothingFactor() {

		return this.smoothing;

	}

	/**
	 * Sets the luminance threshold smoothing factor.
	 *
	 * @deprecated Use smoothing instead.
	 * @param {Number} value - The smoothing factor.
	 */

	setSmoothingFactor(value) {

		this.smoothing = value;

	}

	/**
	 * Indicates whether the luminance threshold is enabled.
	 *
	 * @type {Boolean}
	 * @deprecated Adjust the threshold or smoothing factor instead.
	 */

	get useThreshold() {

		return (this.threshold > 0 || this.smoothing > 0);

	}

	set useThreshold(value) {}

	/**
	 * Indicates whether color output is enabled.
	 *
	 * @type {Boolean}
	 */

	get colorOutput() {

		return (this.defines.COLOR !== undefined);

	}

	set colorOutput(value) {

		if(value) {

			this.defines.COLOR = "1";

		} else {

			delete this.defines.COLOR;

		}

		this.needsUpdate = true;

	}

	/**
	 * Indicates whether color output is enabled.
	 *
	 * @deprecated Use colorOutput instead.
	 * @return {Boolean} Whether color output is enabled.
	 */

	isColorOutputEnabled(value) {

		return this.colorOutput;

	}

	/**
	 * Enables or disables color output.
	 *
	 * @deprecated Use colorOutput instead.
	 * @param {Boolean} value - Whether color output should be enabled.
	 */

	setColorOutputEnabled(value) {

		this.colorOutput = value;

	}

	/**
	 * Indicates whether luminance masking is enabled.
	 *
	 * @type {Boolean}
	 * @deprecated
	 */

	get useRange() {

		return (this.luminanceRange !== null);

	}

	set useRange(value) {

		this.luminanceRange = null;

	}

	/**
	 * The luminance range. Set to null to disable.
	 *
	 * @type {Boolean}
	 */

	get luminanceRange() {

		return this.uniforms.range.value;

	}

	set luminanceRange(value) {

		if(value !== null) {

			this.defines.RANGE = "1";

		} else {

			delete this.defines.RANGE;

		}

		this.uniforms.range.value = value;
		this.needsUpdate = true;

	}

	/**
	 * Returns the current luminance range.
	 *
	 * @deprecated Use luminanceRange instead.
	 * @return {Vector2} The luminance range.
	 */

	getLuminanceRange() {

		return this.luminanceRange;

	}

	/**
	 * Sets a luminance range. Set to null to disable.
	 *
	 * @deprecated Use luminanceRange instead.
	 * @param {Vector2} value - The luminance range.
	 */

	setLuminanceRange(value) {

		this.luminanceRange = value;

	}

}