Home Reference Source

src/effects/ScanlineEffect.js

import { Uniform, Vector2 } from "three";
import { BlendFunction } from "../enums/BlendFunction.js";
import { Effect } from "./Effect.js";

import fragmentShader from "./glsl/scanlines.frag";

/**
 * A scanline effect.
 *
 * Based on an implementation by Georg 'Leviathan' Steinrohder (CC BY 3.0):
 * http://www.truevision3d.com/forums/showcase/staticnoise_colorblackwhite_scanline_shaders-t18698.0.html
 */

export class ScanlineEffect extends Effect {

	/**
	 * Constructs a new scanline effect.
	 *
	 * @param {Object} [options] - The options.
	 * @param {BlendFunction} [options.blendFunction=BlendFunction.OVERLAY] - The blend function of this effect.
	 * @param {Number} [options.density=1.25] - The scanline density.
	 * @param {Number} [options.scrollSpeed=0.0] - The scanline scroll speed.
	 */

	constructor({ blendFunction = BlendFunction.OVERLAY, density = 1.25, scrollSpeed = 0.0 } = {}) {

		super("ScanlineEffect", fragmentShader, {
			blendFunction,
			uniforms: new Map([
				["count", new Uniform(0.0)],
				["scrollSpeed", new Uniform(0.0)]
			])
		});

		/**
		 * The original resolution.
		 *
		 * @type {Vector2}
		 * @private
		 */

		this.resolution = new Vector2();

		/**
		 * The amount of scanlines, relative to the screen height.
		 *
		 * @type {Number}
		 * @private
		 */

		this.d = density;

		this.scrollSpeed = scrollSpeed;

	}

	/**
	 * The scanline density.
	 *
	 * @type {Number}
	 */

	get density() {

		return this.d;

	}

	set density(value) {

		this.d = value;
		this.setSize(this.resolution.width, this.resolution.height);

	}

	/**
	 * Returns the current scanline density.
	 *
	 * @deprecated Use density instead.
	 * @return {Number} The scanline density.
	 */

	getDensity() {

		return this.density;

	}

	/**
	 * Sets the scanline density.
	 *
	 * @deprecated Use density instead.
	 * @param {Number} value - The new scanline density.
	 */

	setDensity(value) {

		this.density = value;

	}

	/**
	 * The scanline scroll speed. Default is 0 (disabled).
	 *
	 * @type {Number}
	 */

	get scrollSpeed() {

		return this.uniforms.get("scrollSpeed").value;

	}

	set scrollSpeed(value) {

		this.uniforms.get("scrollSpeed").value = value;

		if(value === 0) {

			if(this.defines.delete("SCROLL")) {

				this.setChanged();

			}

		} else if(!this.defines.has("SCROLL")) {

			this.defines.set("SCROLL", "1");
			this.setChanged();

		}

	}

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

	setSize(width, height) {

		this.resolution.set(width, height);
		this.uniforms.get("count").value = Math.round(height * this.density);

	}

}