Home Reference Source

src/materials/SMAAWeightsMaterial.js

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

import fragmentShader from "./glsl/smaa-weights.frag";
import vertexShader from "./glsl/smaa-weights.vert";

/**
 * Subpixel Morphological Antialiasing.
 *
 * This material computes weights for detected edges.
 *
 * @implements {Resizable}
 */

export class SMAAWeightsMaterial extends ShaderMaterial {

	/**
	 * Constructs a new SMAA weights material.
	 *
	 * @param {Vector2} [texelSize] - The absolute screen texel size.
	 * @param {Vector2} [resolution] - The resolution.
	 */

	constructor(texelSize = new Vector2(), resolution = new Vector2()) {

		super({
			name: "SMAAWeightsMaterial",
			defines: {
				// Configurable settings:
				MAX_SEARCH_STEPS_INT: "16",
				MAX_SEARCH_STEPS_FLOAT: "16.0",
				MAX_SEARCH_STEPS_DIAG_INT: "8",
				MAX_SEARCH_STEPS_DIAG_FLOAT: "8.0",
				CORNER_ROUNDING: "25",
				CORNER_ROUNDING_NORM: "0.25",
				// Non-configurable settings:
				AREATEX_MAX_DISTANCE: "16.0",
				AREATEX_MAX_DISTANCE_DIAG: "20.0",
				AREATEX_PIXEL_SIZE: "(1.0 / vec2(160.0, 560.0))",
				AREATEX_SUBTEX_SIZE: "(1.0 / 7.0)",
				SEARCHTEX_SIZE: "vec2(66.0, 33.0)",
				SEARCHTEX_PACKED_SIZE: "vec2(64.0, 16.0)"
			},
			uniforms: {
				inputBuffer: new Uniform(null),
				searchTexture: new Uniform(null),
				areaTexture: new Uniform(null),
				resolution: new Uniform(resolution),
				texelSize: new Uniform(texelSize)
			},
			blending: NoBlending,
			toneMapped: false,
			depthWrite: false,
			depthTest: false,
			fragmentShader,
			vertexShader
		});

	}

	/**
	 * 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 search lookup texture.
	 *
	 * @type {Texture}
	 */

	get searchTexture() {

		return this.uniforms.searchTexture.value;

	}

	set searchTexture(value) {

		this.uniforms.searchTexture.value = value;

	}

	/**
	 * The area lookup texture.
	 *
	 * @type {Texture}
	 */

	get areaTexture() {

		return this.uniforms.areaTexture.value;

	}

	set areaTexture(value) {

		this.uniforms.areaTexture.value = value;

	}

	/**
	 * Sets the search and area lookup textures.
	 *
	 * @deprecated Use searchTexture and areaTexture instead.
	 * @param {Texture} search - The search lookup texture.
	 * @param {Texture} area - The area lookup texture.
	 */

	setLookupTextures(search, area) {

		this.searchTexture = search;
		this.areaTexture = area;

	}

	/**
	 * The maximum amount of steps performed in the horizontal/vertical pattern searches, at each side of the pixel.
	 * Range: [0, 112].
	 *
	 * In number of pixels, it's actually the double. So the maximum line length perfectly handled by, for example 16, is
	 * 64 (perfectly means that longer lines won't look as good, but are still antialiased).
	 *
	 * @type {Number}
	 */

	get orthogonalSearchSteps() {

		return Number(this.defines.MAX_SEARCH_STEPS_INT);

	}

	set orthogonalSearchSteps(value) {

		const s = Math.min(Math.max(value, 0), 112);
		this.defines.MAX_SEARCH_STEPS_INT = s.toFixed("0");
		this.defines.MAX_SEARCH_STEPS_FLOAT = s.toFixed("1");
		this.needsUpdate = true;

	}

	/**
	 * Sets the maximum amount of steps performed in the horizontal/vertical pattern searches, at each side of the pixel.
	 *
	 * @deprecated Use orthogonalSearchSteps instead.
	 * @param {Number} value - The search steps. Range: [0, 112].
	 */

	setOrthogonalSearchSteps(value) {

		this.orthogonalSearchSteps = value;

	}

	/**
	 * The maximum steps performed in the diagonal pattern searches, at each side of the pixel. This search
	 * jumps one pixel at a time. Range: [0, 20].
	 *
	 * On high-end machines this search is cheap (between 0.8x and 0.9x slower for 16 steps), but it can have a
	 * significant impact on older machines.
	 *
	 * @type {Number}
	 */

	get diagonalSearchSteps() {

		return Number(this.defines.MAX_SEARCH_STEPS_DIAG_INT);

	}

	set diagonalSearchSteps(value) {

		const s = Math.min(Math.max(value, 0), 20);
		this.defines.MAX_SEARCH_STEPS_DIAG_INT = s.toFixed("0");
		this.defines.MAX_SEARCH_STEPS_DIAG_FLOAT = s.toFixed("1");
		this.needsUpdate = true;

	}

	/**
	 * Specifies the maximum steps performed in the diagonal pattern searches, at each side of the pixel.
	 *
	 * @deprecated Use diagonalSearchSteps instead.
	 * @param {Number} value - The search steps. Range: [0, 20].
	 */

	setDiagonalSearchSteps(value) {

		this.diagonalSearchSteps = value;

	}

	/**
	 * Indicates whether diagonal pattern detection is enabled.
	 *
	 * @type {Boolean}
	 */

	get diagonalDetection() {

		return (this.defines.DISABLE_DIAG_DETECTION === undefined);

	}

	set diagonalDetection(value) {

		if(value) {

			delete this.defines.DISABLE_DIAG_DETECTION;

		} else {

			this.defines.DISABLE_DIAG_DETECTION = "1";

		}

		this.needsUpdate = true;

	}

	/**
	 * Indicates whether diagonal pattern detection is enabled.
	 *
	 * @deprecated Use diagonalDetection instead.
	 * @return {Boolean} Whether diagonal pattern detection is enabled.
	 */

	isDiagonalDetectionEnabled() {

		return this.diagonalDetection;

	}

	/**
	 * Enables or disables diagonal pattern detection.
	 *
	 * @deprecated Use diagonalDetection instead.
	 * @param {Boolean} value - Whether diagonal pattern detection should be enabled.
	 */

	setDiagonalDetectionEnabled(value) {

		this.diagonalDetection = value;

	}

	/**
	 * Specifies how much sharp corners will be rounded. Range: [0, 100].
	 *
	 * @type {Number}
	 */

	get cornerRounding() {

		return Number(this.defines.CORNER_ROUNDING);

	}

	set cornerRounding(value) {

		const r = Math.min(Math.max(value, 0), 100);
		this.defines.CORNER_ROUNDING = r.toFixed("4");
		this.defines.CORNER_ROUNDING_NORM = (r / 100.0).toFixed("4");
		this.needsUpdate = true;

	}

	/**
	 * Specifies how much sharp corners will be rounded.
	 *
	 * @deprecated Use cornerRounding instead.
	 * @param {Number} value - The corner rounding amount. Range: [0, 100].
	 */

	setCornerRounding(value) {

		this.cornerRounding = value;

	}

	/**
	 * Indicates whether corner detection is enabled.
	 *
	 * @type {Number}
	 */

	get cornerDetection() {

		return (this.defines.DISABLE_CORNER_DETECTION === undefined);

	}

	set cornerDetection(value) {

		if(value) {

			delete this.defines.DISABLE_CORNER_DETECTION;

		} else {

			this.defines.DISABLE_CORNER_DETECTION = "1";

		}

		this.needsUpdate = true;

	}

	/**
	 * Indicates whether corner rounding is enabled.
	 *
	 * @deprecated Use cornerDetection instead.
	 * @return {Boolean} Whether corner rounding is enabled.
	 */

	isCornerRoundingEnabled() {

		return this.cornerDetection;

	}

	/**
	 * Enables or disables corner rounding.
	 *
	 * @deprecated Use cornerDetection instead.
	 * @param {Boolean} value - Whether corner rounding should be enabled.
	 */

	setCornerRoundingEnabled(value) {

		this.cornerDetection = value;

	}

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

	setSize(width, height) {

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

	}

}