Home Reference Source

src/materials/DepthMaskMaterial.js

import {
	AlwaysDepth,
	BasicDepthPacking,
	EqualDepth,
	GreaterDepth,
	GreaterEqualDepth,
	LessDepth,
	LessEqualDepth,
	NeverDepth,
	NoBlending,
	NotEqualDepth,
	PerspectiveCamera,
	ShaderMaterial,
	Uniform,
	Vector2
} from "three";

import { DepthTestStrategy } from "../enums/DepthTestStrategy.js";

import fragmentShader from "./glsl/depth-mask.frag";
import vertexShader from "./glsl/common.vert";

/**
 * A depth mask shader material.
 *
 * This material masks a color buffer by comparing two depth textures.
 */

export class DepthMaskMaterial extends ShaderMaterial {

	/**
	 * Constructs a new depth mask material.
	 */

	constructor() {

		super({
			name: "DepthMaskMaterial",
			defines: {
				DEPTH_EPSILON: "0.0001",
				DEPTH_PACKING_0: "0",
				DEPTH_PACKING_1: "0",
				DEPTH_TEST_STRATEGY: DepthTestStrategy.KEEP_MAX_DEPTH
			},
			uniforms: {
				inputBuffer: new Uniform(null),
				depthBuffer0: new Uniform(null),
				depthBuffer1: new Uniform(null),
				cameraNearFar: new Uniform(new Vector2(1, 1))
			},
			blending: NoBlending,
			toneMapped: false,
			depthWrite: false,
			depthTest: false,
			fragmentShader,
			vertexShader
		});

		this.depthMode = LessDepth;

	}

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

	set depthBuffer0(value) {

		this.uniforms.depthBuffer0.value = value;

	}

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

	set depthPacking0(value) {

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

	}

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

	setDepthBuffer0(buffer, depthPacking = BasicDepthPacking) {

		this.depthBuffer0 = buffer;
		this.depthPacking0 = depthPacking;

	}

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

	set depthBuffer1(value) {

		this.uniforms.depthBuffer1.value = value;

	}

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

	set depthPacking1(value) {

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

	}

	/**
	 * Sets the depth buffer that will be compared with the base depth buffer.
	 *
	 * @deprecated Use depthBuffer1 and depthPacking1 instead.
	 * @param {Texture} buffer - The depth texture.
	 * @param {DepthPackingStrategies} [depthPacking=BasicDepthPacking] - The depth packing strategy.
	 */

	setDepthBuffer1(buffer, depthPacking = BasicDepthPacking) {

		this.depthBuffer1 = buffer;
		this.depthPacking1 = depthPacking;

	}

	/**
	 * The strategy for handling maximum depth.
	 *
	 * @type {DepthTestStrategy}
	 */

	get maxDepthStrategy() {

		return Number(this.defines.DEPTH_TEST_STRATEGY);

	}

	set maxDepthStrategy(value) {

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

	}

	/**
	 * Indicates whether maximum depth values should be preserved.
	 *
	 * @type {Boolean}
	 * @deprecated Use maxDepthStrategy instead.
	 */

	get keepFar() {

		return this.maxDepthStrategy;

	}

	set keepFar(value) {

		this.maxDepthStrategy = value ? DepthTestStrategy.KEEP_MAX_DEPTH : DepthTestStrategy.DISCARD_MAX_DEPTH;

	}

	/**
	 * Returns the strategy for dealing with maximum depth values.
	 *
	 * @deprecated Use maxDepthStrategy instead.
	 * @return {DepthTestStrategy} The strategy.
	 */

	getMaxDepthStrategy() {

		return this.maxDepthStrategy;

	}

	/**
	 * Sets the strategy for dealing with maximum depth values.
	 *
	 * @deprecated Use maxDepthStrategy instead.
	 * @param {DepthTestStrategy} value - The strategy.
	 */

	setMaxDepthStrategy(value) {

		this.maxDepthStrategy = value;

	}

	/**
	 * A small error threshold that is used for `EqualDepth` and `NotEqualDepth` tests. Default is `1e-4`.
	 *
	 * @type {Number}
	 */

	get epsilon() {

		return Number(this.defines.DEPTH_EPSILON);

	}

	set epsilon(value) {

		this.defines.DEPTH_EPSILON = value.toFixed(16);
		this.needsUpdate = true;

	}

	/**
	 * Returns the current error threshold for depth comparisons.
	 *
	 * @deprecated Use epsilon instead.
	 * @return {Number} The error threshold.
	 */

	getEpsilon() {

		return this.epsilon;

	}

	/**
	 * Sets the depth comparison error threshold.
	 *
	 * @deprecated Use epsilon instead.
	 * @param {Number} value - The new error threshold.
	 */

	setEpsilon(value) {

		this.epsilon = value;

	}

	/**
	 * The depth mode.
	 *
	 * @see https://threejs.org/docs/#api/en/constants/Materials
	 * @type {DepthModes}
	 */

	get depthMode() {

		return Number(this.defines.DEPTH_MODE);

	}

	set depthMode(value) {

		// If the depth test fails, the texel will be discarded.
		let depthTest;

		switch(value) {

			case NeverDepth:
				depthTest = "false";
				break;

			case AlwaysDepth:
				depthTest = "true";
				break;

			case EqualDepth:
				depthTest = "abs(d1 - d0) <= DEPTH_EPSILON";
				break;

			case NotEqualDepth:
				depthTest = "abs(d1 - d0) > DEPTH_EPSILON";
				break;

			case LessDepth:
				depthTest = "d0 > d1";
				break;

			case LessEqualDepth:
				depthTest = "d0 >= d1";
				break;

			case GreaterEqualDepth:
				depthTest = "d0 <= d1";
				break;

			case GreaterDepth:
			default:
				depthTest = "d0 < d1";
				break;

		}

		this.defines.DEPTH_MODE = value.toFixed(0);
		this.defines["depthTest(d0, d1)"] = depthTest;
		this.needsUpdate = true;

	}

	/**
	 * Returns the current depth mode.
	 *
	 * @deprecated Use depthMode instead.
	 * @return {DepthModes} The depth mode. Default is `LessDepth`.
	 */

	getDepthMode() {

		return this.depthMode;

	}

	/**
	 * Sets the depth mode.
	 *
	 * @deprecated Use depthMode instead.
	 * @param {DepthModes} mode - The depth mode.
	 */

	setDepthMode(mode) {

		this.depthMode = mode;

	}

	/**
	 * Copies the settings of the given camera.
	 *
	 * @deprecated Use copyCameraSettings instead.
	 * @param {Camera} camera - A camera.
	 */

	adoptCameraSettings(camera) {

		this.copyCameraSettings(camera);

	}

	/**
	 * Copies the settings of the given camera.
	 *
	 * @param {Camera} camera - A camera.
	 */

	copyCameraSettings(camera) {

		if(camera) {

			this.uniforms.cameraNearFar.value.set(camera.near, camera.far);

			if(camera instanceof PerspectiveCamera) {

				this.defines.PERSPECTIVE_CAMERA = "1";

			} else {

				delete this.defines.PERSPECTIVE_CAMERA;

			}

			this.needsUpdate = true;

		}

	}

}