Home Reference Source

src/materials/EdgeDetectionMaterial.js

import { BasicDepthPacking, NoBlending, REVISION, ShaderMaterial, Uniform, Vector2 } from "three";
import { EdgeDetectionMode } from "../enums/EdgeDetectionMode.js";

import fragmentShader from "./glsl/edge-detection.frag";
import vertexShader from "./glsl/edge-detection.vert";

/**
 * An edge detection material. Mainly used for SMAA.
 *
 * @implements {Resizable}
 */

export class EdgeDetectionMaterial extends ShaderMaterial {

	/**
	 * Constructs a new edge detection material.
	 *
	 * TODO Remove parameters.
	 * @param {Vector2} [texelSize] - The screen texel size.
	 * @param {EdgeDetectionMode} [mode=EdgeDetectionMode.COLOR] - The edge detection mode.
	 */

	constructor(texelSize = new Vector2(), mode = EdgeDetectionMode.COLOR) {

		super({
			name: "EdgeDetectionMaterial",
			defines: {
				THREE_REVISION: REVISION.replace(/\D+/g, ""),
				LOCAL_CONTRAST_ADAPTATION_FACTOR: "2.0",
				EDGE_THRESHOLD: "0.1",
				DEPTH_THRESHOLD: "0.01",
				PREDICATION_MODE: "0",
				PREDICATION_THRESHOLD: "0.01",
				PREDICATION_SCALE: "2.0",
				PREDICATION_STRENGTH: "1.0",
				DEPTH_PACKING: "0"
			},
			uniforms: {
				inputBuffer: new Uniform(null),
				depthBuffer: new Uniform(null),
				predicationBuffer: new Uniform(null),
				texelSize: new Uniform(texelSize)
			},
			blending: NoBlending,
			toneMapped: false,
			depthWrite: false,
			depthTest: false,
			fragmentShader,
			vertexShader
		});

		this.edgeDetectionMode = mode;

	}

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

	set depthBuffer(value) {

		this.uniforms.depthBuffer.value = value;

	}

	/**
	 * The depth packing strategy.
	 *
	 * @type {DepthPackingStrategies}
	 */

	set depthPacking(value) {

		this.defines.DEPTH_PACKING = value.toFixed(0);
		this.needsUpdate = true;

	}

	/**
	 * Sets the depth buffer.
	 *
	 * @deprecated Use depthBuffer and depthPacking instead.
	 * @param {Texture} buffer - The depth texture.
	 * @param {DepthPackingStrategies} [depthPacking=BasicDepthPacking] - The depth packing strategy.
	 */

	setDepthBuffer(buffer, depthPacking = BasicDepthPacking) {

		this.depthBuffer = buffer;
		this.depthPacking = depthPacking;

	}

	/**
	 * The edge detection mode.
	 *
	 * @type {EdgeDetectionMode}
	 */

	get edgeDetectionMode() {

		return Number(this.defines.EDGE_DETECTION_MODE);

	}

	set edgeDetectionMode(value) {

		this.defines.EDGE_DETECTION_MODE = value.toFixed(0);
		this.needsUpdate = true;

	}

	/**
	 * Returns the edge detection mode.
	 *
	 * @deprecated Use edgeDetectionMode instead.
	 * @return {EdgeDetectionMode} The mode.
	 */

	getEdgeDetectionMode() {

		return this.edgeDetectionMode;

	}

	/**
	 * Sets the edge detection mode.
	 *
	 * @deprecated Use edgeDetectionMode instead.
	 * @param {EdgeDetectionMode} value - The edge detection mode.
	 */

	setEdgeDetectionMode(value) {

		this.edgeDetectionMode = value;

	}

	/**
	 * The local contrast adaptation factor. Has no effect if the edge detection mode is set to DEPTH. Default is 2.0.
	 *
	 * If a neighbor edge has _factor_ times bigger contrast than the current edge, the edge will be discarded.
	 *
	 * This allows to eliminate spurious crossing edges and is based on the fact that if there is too much contrast in a
	 * direction, the perceptual contrast in the other neighbors will be hidden.
	 *
	 * @type {Number}
	 */

	get localContrastAdaptationFactor() {

		return Number(this.defines.LOCAL_CONTRAST_ADAPTATION_FACTOR);

	}

	set localContrastAdaptationFactor(value) {

		this.defines.LOCAL_CONTRAST_ADAPTATION_FACTOR = value.toFixed("6");
		this.needsUpdate = true;

	}

	/**
	 * Returns the local contrast adaptation factor.
	 *
	 * @deprecated Use localContrastAdaptationFactor instead.
	 * @return {Number} The factor.
	 */

	getLocalContrastAdaptationFactor() {

		return this.localContrastAdaptationFactor;

	}

	/**
	 * Sets the local contrast adaptation factor. Has no effect if the edge detection mode is set to DEPTH.
	 *
	 * @deprecated Use localContrastAdaptationFactor instead.
	 * @param {Number} value - The local contrast adaptation factor. Default is 2.0.
	 */

	setLocalContrastAdaptationFactor(value) {

		this.localContrastAdaptationFactor = value;

	}

	/**
	 * The edge detection threshold. Range: [0.0, 0.5].
	 *
	 * A lower value results in more edges being detected at the expense of performance.
	 *
	 * For luma- and chroma-based edge detection, 0.1 is a reasonable value and allows to catch most visible edges. 0.05
	 * is a rather overkill value that allows to catch 'em all. Darker scenes may require an even lower threshold.
	 *
	 * If depth-based edge detection is used, the threshold will depend on the scene depth.
	 *
	 * @type {Number}
	 */

	get edgeDetectionThreshold() {

		return Number(this.defines.EDGE_THRESHOLD);

	}

	set edgeDetectionThreshold(value) {

		this.defines.EDGE_THRESHOLD = value.toFixed("6");
		this.defines.DEPTH_THRESHOLD = (value * 0.1).toFixed("6");
		this.needsUpdate = true;

	}

	/**
	 * Returns the edge detection threshold.
	 *
	 * @deprecated Use edgeDetectionThreshold instead.
	 * @return {Number} The threshold.
	 */

	getEdgeDetectionThreshold() {

		return this.edgeDetectionThreshold;

	}

	/**
	 * Sets the edge detection threshold.
	 *
	 * @deprecated Use edgeDetectionThreshold instead.
	 * @param {Number} value - The edge detection threshold. Range: [0.0, 0.5].
	 */

	setEdgeDetectionThreshold(value) {

		this.edgeDetectionThreshold = value;

	}

	/**
	 * The predication mode.
	 *
	 * Predicated thresholding allows to better preserve texture details and to improve edge detection using an additional
	 * buffer such as a light accumulation or depth buffer.
	 *
	 * @type {PredicationMode}
	 */

	get predicationMode() {

		return Number(this.defines.PREDICATION_MODE);

	}

	set predicationMode(value) {

		this.defines.PREDICATION_MODE = value.toFixed(0);
		this.needsUpdate = true;

	}

	/**
	 * Returns the predication mode.
	 *
	 * @deprecated Use predicationMode instead.
	 * @return {PredicationMode} The mode.
	 */

	getPredicationMode() {

		return this.predicationMode;

	}

	/**
	 * Sets the predication mode.
	 *
	 * @deprecated Use predicationMode instead.
	 * @param {PredicationMode} value - The predication mode.
	 */

	setPredicationMode(value) {

		this.predicationMode = value;

	}

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

	set predicationBuffer(value) {

		this.uniforms.predicationBuffer.value = value;

	}

	/**
	 * Sets a custom predication buffer.
	 *
	 * @deprecated Use predicationBuffer instead.
	 * @param {Texture} value - The predication buffer.
	 */

	setPredicationBuffer(value) {

		this.uniforms.predicationBuffer.value = value;

	}

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

	get predicationThreshold() {

		return Number(this.defines.PREDICATION_THRESHOLD);

	}

	set predicationThreshold(value) {

		this.defines.PREDICATION_THRESHOLD = value.toFixed("6");
		this.needsUpdate = true;

	}

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

	getPredicationThreshold() {

		return this.predicationThreshold;

	}

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

	setPredicationThreshold(value) {

		this.predicationThreshold = value;

	}

	/**
	 * The predication scale. Range: [1.0, 5.0].
	 *
	 * Determines how much the edge detection threshold should be scaled when using predication.
	 *
	 * @type {Boolean|Texture|Number}
	 */

	get predicationScale() {

		return Number(this.defines.PREDICATION_SCALE);

	}

	set predicationScale(value) {

		this.defines.PREDICATION_SCALE = value.toFixed("6");
		this.needsUpdate = true;

	}

	/**
	 * Returns the predication scale.
	 *
	 * @deprecated Use predicationScale instead.
	 * @return {Number} The scale.
	 */

	getPredicationScale() {

		return this.predicationScale;

	}

	/**
	 * Sets the predication scale.
	 *
	 * @deprecated Use predicationScale instead.
	 * @param {Number} value - The scale. Range: [1.0, 5.0].
	 */

	setPredicationScale(value) {

		this.predicationScale = value;

	}

	/**
	 * The predication strength. Range: [0.0, 1.0].
	 *
	 * Determines how much the edge detection threshold should be decreased locally when using predication.
	 *
	 * @type {Number}
	 */

	get predicationStrength() {

		return Number(this.defines.PREDICATION_STRENGTH);

	}

	set predicationStrength(value) {

		this.defines.PREDICATION_STRENGTH = value.toFixed("6");
		this.needsUpdate = true;

	}

	/**
	 * Returns the predication strength.
	 *
	 * @deprecated Use predicationStrength instead.
	 * @return {Number} The strength.
	 */

	getPredicationStrength() {

		return this.predicationStrength;

	}

	/**
	 * Sets the predication strength.
	 *
	 * @deprecated Use predicationStrength instead.
	 * @param {Number} value - The strength. Range: [0.0, 1.0].
	 */

	setPredicationStrength(value) {

		this.predicationStrength = value;

	}

	/**
	 * Sets the size of this object.
	 *
	 * @param {Number} width - The width.
	 * @param {Number} height - The height.
	 */

	setSize(width, height) {

		this.uniforms.texelSize.value.set(1.0 / width, 1.0 / height);

	}

}