meatbags 7 lat temu
rodzic
commit
a7c590322c

+ 1 - 1
examples/files.js

@@ -229,7 +229,7 @@ var files = {
 		"webgl_postprocessing_fxaa",
 		"webgl_postprocessing_fxaa",
 		"webgl_postprocessing_glitch",
 		"webgl_postprocessing_glitch",
 		"webgl_postprocessing_godrays",
 		"webgl_postprocessing_godrays",
-		"webgl_postprocessing_color_halftone",
+		"webgl_postprocessing_rgb_halftone",
 		"webgl_postprocessing_masking",
 		"webgl_postprocessing_masking",
 		"webgl_postprocessing_ssaa",
 		"webgl_postprocessing_ssaa",
 		"webgl_postprocessing_ssaa_unbiased",
 		"webgl_postprocessing_ssaa_unbiased",

+ 48 - 36
examples/js/postprocessing/HalftonePass.js

@@ -1,49 +1,61 @@
 /**
 /**
  * @author meatbags / xavierburrow.com, github/meatbags
  * @author meatbags / xavierburrow.com, github/meatbags
+ *
+ * RGB Halftone pass for three.js effects composer. Requires THREE.HalftoneShader.
+ *
  */
  */
 
 
-THREE.HalftonePass = function(width, height) {
-	THREE.Pass.call(this);
+THREE.HalftonePass = function ( width, height ) {
 
 
-	this.width = width;
-	this.height = height;
+	THREE.Pass.call( this );
+ 	this.width = width;
+ 	this.height = height;
 
 
-	if (THREE.HalftoneShader === undefined) {
-		console.error('THREE.HalftonePass requires THREE.HalftoneShader');
-	}
+ 	if ( THREE.HalftoneShader === undefined ) {
 
 
-	this.uniforms = THREE.UniformsUtils.clone(THREE.HalftoneShader.uniforms);
-	this.material = new THREE.ShaderMaterial({
-		uniforms: this.uniforms,
-		fragmentShader: THREE.HalftoneShader.fragmentShader,
-		vertexShader: THREE.HalftoneShader.vertexShader
-	});
+ 		console.error( 'THREE.HalftonePass requires THREE.HalftoneShader' );
 
 
-	this.camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);
-	this.scene = new THREE.Scene();
-	this.quad = new THREE.Mesh(new THREE.PlaneBufferGeometry(2, 2), null);
-	this.quad.frustumCulled = false;
-	this.scene.add(this.quad);
-};
+ 	}
 
 
-THREE.HalftonePass.prototype = Object.assign(Object.create(THREE.Pass.prototype), {
-	constructor: THREE.HalftonePass,
+ 	this.uniforms = THREE.UniformsUtils.clone( THREE.HalftoneShader.uniforms );
+ 	this.material = new THREE.ShaderMaterial( {
+ 		uniforms: this.uniforms,
+ 		fragmentShader: THREE.HalftoneShader.fragmentShader,
+ 		vertexShader: THREE.HalftoneShader.vertexShader
+ 	} );
 
 
-	render: function(renderer, writeBuffer, readBuffer, delta, maskActive) {
-		this.material.uniforms["tDiffuse"].value = readBuffer.texture;
-		this.quad.material = this.material;
+ 	this.camera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
+ 	this.scene = new THREE.Scene();
+ 	this.quad = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2 ), null );
+ 	this.quad.frustumCulled = false;
+ 	this.scene.add( this.quad );
+
+ };
+
+ THREE.HalftonePass.prototype = Object.assign( Object.create( THREE.Pass.prototype ), {
+ 	constructor: THREE.HalftonePass,
+ 	render: function ( renderer, writeBuffer, readBuffer, delta, maskActive ) {
+
+ 		this.material.uniforms[ "tDiffuse" ].value = readBuffer.texture;
+ 		this.quad.material = this.material;
+
+ 		if ( this.renderToScreen ) {
+
+ 			renderer.render( this.scene, this.camera );
 
 
-		if (this.renderToScreen) {
-			renderer.render(this.scene, this.camera);
 		} else {
 		} else {
-			renderer.render(this.scene, this.camera, writeBuffer, this.clear);
+
+			renderer.render( this.scene, this.camera, writeBuffer, this.clear );
+
 		}
 		}
-	},
-
-	setSize: function(width, height) {
-		this.width = width;
-		this.height = height;
-		this.uniforms['width'].value = this.width;
-		this.uniforms['height'].value = this.height;
-	}
-});
+
+ 	},
+ 	setSize: function ( width, height ) {
+
+ 		this.width = width;
+ 		this.height = height;
+ 		this.uniforms[ 'width' ].value = this.width;
+ 		this.uniforms[ 'height' ].value = this.height;
+
+ 	}
+} );

+ 303 - 170
examples/js/shaders/HalftoneShader.js

@@ -1,179 +1,312 @@
 /**
 /**
  * @author meatbags / xavierburrow.com, github/meatbags
  * @author meatbags / xavierburrow.com, github/meatbags
  *
  *
- * Colour halftone shader
- * Shape uniform (1 = circle, 2 = euclidean dot, 3 = ellipse, 4 = line, 5 = square)
- * Blending mode (1 = linear, 2 = add, 3 = multiply, 4 = lighter colour, 5 = darker colour)
+ * RGB Halftone shader for three.js.
+ *
  */
  */
 
 
 THREE.HalftoneShader = {
 THREE.HalftoneShader = {
+
 	uniforms: {
 	uniforms: {
-		"shape": {value: 2},
-		"tDiffuse": {value: null},
-		"radius": {value: 12},
-		"rC": {value: Math.PI / 12},
-		"rM": {value: Math.PI / 12 * 2},
-		"rY": {value: Math.PI / 12 * 3},
-		"width": {value: 1},
-		"height": {value: 1},
-		"blending": {value: 0},
-		"blendingMode": {value: 1},
-		"greyscale": {value: false},
-		"disable": {value: false}
+		"tDiffuse": { value: null },
+		"shape": { value: 1 },
+		"radius": { value: 4 },
+		"rotateR": { value: Math.PI / 12 * 1 },
+		"rotateG": { value: Math.PI / 12 * 2 },
+		"rotateB": { value: Math.PI / 12 * 3 },
+		"scatter": { value: 0 },
+		"width": { value: 1 },
+		"height": { value: 1 },
+		"blending": { value: 1 },
+		"blendingMode": { value: 1 },
+		"greyscale": { value: false },
+		"disable": { value: false }
 	},
 	},
-	vertexShader: `
-    varying vec2 vUV;
-
-    void main() {
-      vUV = uv;
-      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
-    }`,
-	fragmentShader: `
-		#define SQRT2 1.41421356
-		#define SQRT2_HALF 0.70710678
-		#define SQRT2_MINUS_ONE 0.41421356
-		#define SQRT2_HALF_MINUS_ONE 0.20710678
-
-		uniform sampler2D tDiffuse;
-		uniform float radius;
-		uniform float rC;
-		uniform float rM;
-		uniform float rY;
-		uniform float width;
-		uniform float height;
-		uniform int shape;
-		uniform bool disable;
-		uniform float blending;
-		uniform int blendingMode;
-		uniform bool greyscale;
-
-		varying vec2 vUV;
-
-		float blend(float a, float b, float t) {
-			return a * (1.0 - t) + b * t;
-		}
-
-		float blendColour(float a, float b, float t) {
-			if (blendingMode == 1) {
-				return blend(a, b, t);
-			} else if (blendingMode == 2) {
-				return blend(a, min(1.0, a + b), t);
-			} else if (blendingMode == 3) {
-				return blend(a, max(0.0, a * b), t);
-			} else if (blendingMode == 4) {
-				return blend(a, max(a, b), t);
-			} else {
-				return blend(a, min(a, b), t);
-			}
-		}
-
-		float hypot(float x, float y) {
-			return sqrt(x * x + y * y);
-		}
-
-		float distanceTo(vec2 a, vec2 b) {
-			return hypot(b.x - a.x, b.y - a.y);
-		}
-
-		float shapeDistance(vec2 p, vec2 coord, vec2 n) {
-			float d = distanceTo(p, coord);
-
-			if (shape == 3) {
-				// ellipse
-				if (d != 0.0) {
-					float dp = abs((p.x - coord.x) / d * n.x + (p.y - coord.y) / d * n.y);
-					d = (d * (1.0 - SQRT2_HALF_MINUS_ONE)) + dp * d * SQRT2_MINUS_ONE;
-				}
-			} else if (shape == 4) {
-				// line
-				float dp = (p.x - coord.x) * n.x + (p.y - coord.y) * n.y;
-				d = hypot(n.x * dp, n.y * dp);
-			}
-
-			return d;
-		}
-
-		float shapeRadius(float r, vec2 p, vec2 coord, float angle) {
-			r = pow(abs(r), 1.125);
-
-			if (shape == 2 || shape == 5) {
-				float theta = atan(p.y - coord.y, p.x - coord.x) - angle;
-				float sin_t = abs(sin(theta));
-				float cos_t = abs(cos(theta));
-
-				if (shape == 2) {
-					// euclidean dot
-					float square = r + 2.0 * ((sin_t > cos_t) ? r - sin_t * r : r - cos_t * r);
-
-					if (r <= 0.5) {
-						r = blend(r, square, r * 2.0);
-					} else {
-						r = 0.0;
-						//float r_max = 0.0;
-						//r = blend(square, r_max, pow(abs((r - 0.5) * 2.0), 0.4));
-					}
-				} else {
-					// square
-					r += (sin_t > cos_t) ? r - sin_t * r : r - cos_t * r;
-				}
-			}
-
-			return r;
-		}
-
-		vec2 gridReference(vec2 p, vec2 origin, vec2 n, float step) {
-			// get nearest grid reference (rotated grid)
-			float dot_normal = n.x * (p.x - origin.x) + n.y * (p.y - origin.y);
-			float dot_line = -n.y * (p.x - origin.x) + n.x * (p.y - origin.y);
-			vec2 offset = vec2(n.x * dot_normal, n.y * dot_normal);
-			float offset_normal = mod(hypot(offset.x, offset.y), step);
-			float normal_scale = ((offset_normal < step * 0.5) ? -offset_normal : step - offset_normal) * ((dot_normal < 0.0) ? 1.0 : -1.0);
-			float offset_line = mod(hypot((p.x - offset.x) - origin.x, (p.y - offset.y) - origin.y), step);
-			float line_scale = ((offset_line < step * 0.5) ? -offset_line : step - offset_line) * ((dot_line < 0.0) ? 1.0 : -1.0);
-
-			return vec2(
-				p.x - n.x * normal_scale + n.y * line_scale,
-				p.y - n.y * normal_scale - n.x * line_scale
-			);
-		}
-
-		void main() {
-			if (!disable) {
-				vec2 p = vec2(vUV.x * width, vUV.y * height);
-				vec2 origin = vec2(0, 0);
-				float blur = (radius < 2.0) ? radius * 0.5 : 1.0;
-				vec2 n_c = vec2(cos(rC), sin(rC));
-				vec2 n_m = vec2(cos(rM), sin(rM));
-				vec2 n_y = vec2(cos(rY), sin(rY));
-
-				// sampling
-				vec2 p_c = gridReference(p, origin, n_c, radius);
-				vec2 p_m = gridReference(p, origin, n_m, radius);
-				vec2 p_y = gridReference(p, origin, n_y, radius);
-				float dist_c = shapeRadius(texture2D(tDiffuse, vec2(p_c.x / width, p_c.y / height)).r, p, p_c, rC) * radius - shapeDistance(p, p_c, n_c);
-				float dist_m = shapeRadius(texture2D(tDiffuse, vec2(p_m.x / width, p_m.y / height)).g, p, p_m, rM) * radius - shapeDistance(p, p_m, n_m);
-				float dist_y = shapeRadius(texture2D(tDiffuse, vec2(p_y.x / width, p_y.y / height)).b, p, p_y, rY) * radius - shapeDistance(p, p_y, n_y);
-				float r = (dist_c > 0.0) ? clamp(dist_c / blur, 0.0, 1.0) : 0.0;
-				float g = (dist_m > 0.0) ? clamp(dist_m / blur, 0.0, 1.0) : 0.0;
-				float b = (dist_y > 0.0) ? clamp(dist_y / blur, 0.0, 1.0) : 0.0;
-
-				if (blending != 0.0) {
-					vec4 colour = texture2D(tDiffuse, vUV);
-					r = blendColour(r, colour.r, blending);
-					g = blendColour(g, colour.g, blending);
-					b = blendColour(b, colour.b, blending);
-				}
-
-				if (greyscale) {
-					r = (r + b + g) / 3.0;
-					g = r;
-					b = r;
-				}
-
-				// write
-				gl_FragColor = vec4(r, g, b, 1.0);
-			} else {
-				gl_FragColor = texture2D(tDiffuse, vUV);
-			}
-		}`
+
+	vertexShader: [
+
+    "varying vec2 vUV;",
+
+    "void main() {",
+
+      "vUV = uv;",
+      "gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);",
+
+    "}"
+
+	].join( "\n" ),
+
+	fragmentShader: [
+
+		"#define SQRT2_MINUS_ONE 0.41421356",
+		"#define SQRT2_HALF_MINUS_ONE 0.20710678",
+		"#define SHAPE_DOT 1",
+		"#define PI2 6.28318531",
+		"#define SHAPE_ELLIPSE 2",
+		"#define SHAPE_SQUARE 4",
+		"#define BLENDING_LINEAR 1",
+		"#define SHAPE_LINE 3",
+		"#define BLENDING_MULTIPLY 2",
+		"#define BLENDING_ADD 3",
+		"#define BLENDING_LIGHTER 4",
+		"#define BLENDING_DARKER 5",
+		"uniform sampler2D tDiffuse;",
+		"uniform float radius;",
+		"uniform float rotateR;",
+		"uniform float rotateG;",
+		"uniform float rotateB;",
+		"uniform float scatter;",
+		"uniform float width;",
+		"uniform float height;",
+		"uniform int shape;",
+		"uniform bool disable;",
+		"uniform float blending;",
+		"uniform int blendingMode;",
+		"varying vec2 vUV;",
+		"uniform bool greyscale;",
+		"const int samples = 8;",
+
+		"float blend( float a, float b, float t ) {",
+
+			// linear blend
+			"return a * ( 1.0 - t ) + b * t;",
+
+		"}",
+
+		"float hypot( float x, float y ) {",
+
+			// vector magnitude
+			"return sqrt( x * x + y * y );",
+
+		"}",
+
+		"float rand( vec2 seed ){",
+
+			// get pseudo-random number
+	    "return fract( sin( dot( seed.xy, vec2( 12.9898, 78.233 ) ) ) * 43758.5453 );",
+
+		"}",
+
+		"float distanceToDotRadius( float channel, vec2 coord, vec2 normal, vec2 p, float angle, float rad_max ) {",
+
+			// apply shape-specific transforms
+			"float dist = hypot( coord.x - p.x, coord.y - p.y );",
+			"float rad = channel;",
+
+			"if ( shape == SHAPE_DOT ) {",
+
+				"rad = pow( abs( rad ), 1.125 ) * rad_max;",
+
+			"} else if ( shape == SHAPE_ELLIPSE ) {",
+
+				"rad = pow( abs( rad ), 1.125 ) * rad_max;",
+
+				"if ( dist != 0.0 ) {",
+					"float dot_p = abs( ( p.x - coord.x ) / dist * normal.x + ( p.y - coord.y ) / dist * normal.y );",
+					"dist = ( dist * ( 1.0 - SQRT2_HALF_MINUS_ONE ) ) + dot_p * dist * SQRT2_MINUS_ONE;",
+				"}",
+
+			"} else if ( shape == SHAPE_LINE ) {",
+
+				"rad = pow( abs( rad ), 1.5) * rad_max;",
+				"float dot_p = ( p.x - coord.x ) * normal.x + ( p.y - coord.y ) * normal.y;",
+				"dist = hypot( normal.x * dot_p, normal.y * dot_p );",
+
+			"} else if ( shape == SHAPE_SQUARE ) {",
+
+				"float theta = atan( p.y - coord.y, p.x - coord.x ) - angle;",
+				"float sin_t = abs( sin( theta ) );",
+				"float cos_t = abs( cos( theta ) );",
+				"rad = pow( abs( rad ), 1.4 );",
+				"rad = rad_max * ( rad + ( ( sin_t > cos_t ) ? rad - sin_t * rad : rad - cos_t * rad ) );",
+
+			"}",
+
+			"return rad - dist;",
+
+		"}",
+
+		"struct Cell {",
+
+			// grid sample positions
+			"vec2 normal;",
+			"vec2 p1;",
+			"vec2 p2;",
+			"vec2 p3;",
+			"vec2 p4;",
+			"float samp2;",
+			"float samp1;",
+			"float samp3;",
+			"float samp4;",
+
+		"};",
+
+		"vec4 getSample( vec2 point ) {",
+
+			// multi-sampled point
+			"vec4 tex = texture2D( tDiffuse, vec2( point.x / width, point.y / height ) );",
+			"float base = rand( vec2( floor( point.x ), floor( point.y ) ) ) * PI2;",
+			"float step = PI2 / float( samples );",
+			"float dist = radius * 0.66;",
+
+			"for ( int i = 0; i < samples; ++i ) {",
+
+				"float r = base + step * float( i );",
+				"vec2 coord = point + vec2( cos( r ) * dist, sin( r ) * dist );",
+				"tex += texture2D( tDiffuse, vec2( coord.x / width, coord.y / height ) );",
+
+			"}",
+
+			"tex /= float( samples ) + 1.0;",
+			"return tex;",
+
+		"}",
+
+		"float getDotColour( Cell c, vec2 p, int channel, float angle, float aa ) {",
+
+			// get colour for given point
+			"float dist_c_1, dist_c_2, dist_c_3, dist_c_4, res;",
+
+			"if ( channel == 0 ) {",
+
+				"c.samp1 = getSample( c.p1 ).r;",
+				"c.samp2 = getSample( c.p2 ).r;",
+				"c.samp3 = getSample( c.p3 ).r;",
+				"c.samp4 = getSample( c.p4 ).r;",
+
+			"} else if (channel == 1) {",
+
+				"c.samp1 = getSample( c.p1 ).g;",
+				"c.samp2 = getSample( c.p2 ).g;",
+				"c.samp3 = getSample( c.p3 ).g;",
+				"c.samp4 = getSample( c.p4 ).g;",
+
+			"} else {",
+
+				"c.samp1 = getSample( c.p1 ).b;",
+				"c.samp3 = getSample( c.p3 ).b;",
+				"c.samp2 = getSample( c.p2 ).b;",
+				"c.samp4 = getSample( c.p4 ).b;",
+
+			"}",
+
+			"dist_c_1 = distanceToDotRadius( c.samp1, c.p1, c.normal, p, angle, radius );",
+			"dist_c_2 = distanceToDotRadius( c.samp2, c.p2, c.normal, p, angle, radius );",
+			"dist_c_3 = distanceToDotRadius( c.samp3, c.p3, c.normal, p, angle, radius );",
+			"dist_c_4 = distanceToDotRadius( c.samp4, c.p4, c.normal, p, angle, radius );",
+			"res = ( dist_c_1 > 0.0 ) ? clamp( dist_c_1 / aa, 0.0, 1.0 ) : 0.0;",
+			"res += ( dist_c_2 > 0.0 ) ? clamp( dist_c_2 / aa, 0.0, 1.0 ) : 0.0;",
+			"res += ( dist_c_3 > 0.0 ) ? clamp( dist_c_3 / aa, 0.0, 1.0 ) : 0.0;",
+			"res += ( dist_c_4 > 0.0 ) ? clamp( dist_c_4 / aa, 0.0, 1.0 ) : 0.0;",
+			"res = clamp( res, 0.0, 1.0 );",
+
+			"return res;",
+
+		"}",
+
+		"Cell getReferenceCell( vec2 p, vec2 origin, float grid_angle, float step ) {",
+
+			// get containing cell
+			"Cell c;",
+
+			// calc grid
+			"vec2 n = vec2( cos( grid_angle ), sin( grid_angle ) );",
+			"float threshold = step * 0.5;",
+			"float dot_normal = n.x * ( p.x - origin.x ) + n.y * ( p.y - origin.y );",
+			"float dot_line = -n.y * ( p.x - origin.x ) + n.x * ( p.y - origin.y );",
+			"vec2 offset = vec2( n.x * dot_normal, n.y * dot_normal );",
+			"float offset_normal = mod( hypot( offset.x, offset.y ), step );",
+			"float normal_dir = ( dot_normal < 0.0 ) ? 1.0 : -1.0;",
+			"float normal_scale = ( ( offset_normal < threshold ) ? -offset_normal : step - offset_normal ) * normal_dir;",
+			"float offset_line = mod( hypot( ( p.x - offset.x ) - origin.x, ( p.y - offset.y ) - origin.y ), step );",
+			"float line_dir = ( dot_line < 0.0 ) ? 1.0 : -1.0;",
+			"float line_scale = ( ( offset_line < threshold ) ? -offset_line : step - offset_line ) * line_dir;",
+
+			// get closest corner
+			"c.normal = n;",
+			"c.p1.x = p.x - n.x * normal_scale + n.y * line_scale;",
+			"c.p1.y = p.y - n.y * normal_scale - n.x * line_scale;",
+
+			// scatter
+			"if ( scatter != 0.0 ) {",
+
+				"float off_mag = scatter * threshold * 0.5;",
+				"float off_angle = rand( vec2( floor( c.p1.x ), floor( c.p1.y ) ) ) * PI2;",
+				"c.p1.x += cos( off_angle ) * off_mag;",
+				"c.p1.y += sin( off_angle ) * off_mag;",
+
+			"}",
+
+			// find corners
+			"float normal_step = normal_dir * ( ( offset_normal < threshold ) ? step : -step );",
+			"float line_step = line_dir * ( ( offset_line < threshold ) ? step : -step );",
+			"c.p2.x = c.p1.x - n.x * normal_step;",
+			"c.p2.y = c.p1.y - n.y * normal_step;",
+			"c.p3.x = c.p1.x + n.y * line_step;",
+			"c.p3.y = c.p1.y - n.x * line_step;",
+			"c.p4.x = c.p1.x - n.x * normal_step + n.y * line_step;",
+			"c.p4.y = c.p1.y - n.y * normal_step - n.x * line_step;",
+
+			"return c;",
+
+		"}",
+
+		"float blendColour( float a, float b, float t ) {",
+
+			// blend colours
+			"if ( blendingMode == BLENDING_LINEAR ) {",
+				"return blend( a, b, 1.0 - t );",
+			"} else if ( blendingMode == BLENDING_ADD ) {",
+				"return blend( a, min( 1.0, a + b ), t );",
+			"} else if ( blendingMode == BLENDING_MULTIPLY ) {",
+				"return blend( a, max( 0.0, a * b ), t );",
+			"} else if ( blendingMode == BLENDING_LIGHTER ) {",
+				"return blend( a, max( a, b ), t );",
+			"} else if ( blendingMode == BLENDING_DARKER ) {",
+				"return blend( a, min( a, b ), t );",
+			"} else {",
+				"return blend( a, b, 1.0 - t );",
+			"}",
+
+		"}",
+
+		"void main() {",
+
+			"if ( ! disable ) {",
+
+				// setup
+				"vec2 p = vec2( vUV.x * width, vUV.y * height );",
+				"vec2 origin = vec2( 0, 0 );",
+				"float aa = ( radius < 2.5 ) ? radius * 0.5 : 1.25;",
+
+				// get channel samples
+				"Cell cell_r = getReferenceCell( p, origin, rotateR, radius );",
+				"Cell cell_g = getReferenceCell( p, origin, rotateG, radius );",
+				"Cell cell_b = getReferenceCell( p, origin, rotateB, radius );",
+				"float r = getDotColour( cell_r, p, 0, rotateR, aa );",
+				"float g = getDotColour( cell_g, p, 1, rotateG, aa );",
+				"float b = getDotColour( cell_b, p, 2, rotateB, aa );",
+
+				// blend with original
+				"vec4 colour = texture2D( tDiffuse, vUV );",
+				"r = blendColour( r, colour.r, blending );",
+				"g = blendColour( g, colour.g, blending );",
+				"b = blendColour( b, colour.b, blending );",
+
+				"if ( greyscale ) {",
+					"r = g = b = (r + b + g) / 3.0;",
+				"}",
+
+				"gl_FragColor = vec4( r, g, b, 1.0 );",
+
+			"} else {",
+
+				"gl_FragColor = texture2D( tDiffuse, vUV );",
+
+			"}",
+
+		"}"
+
+	].join( "\n" )
+
 };
 };

+ 0 - 207
examples/webgl_postprocessing_color_halftone.html

@@ -1,207 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-	<head>
-		<title>three.js webgl</title>
-		<meta charset="utf-8">
-		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
-		<style>
-			body {
-				font-family: Monospace;
-				background-color: #f0f0f0;
-				margin: 0px;
-				overflow: hidden;
-			}
-
-			.info {
-				position: absolute;
-				background-color: black;
-				opacity: 0.8;
-				color: white;
-				text-align: center;
-				top: 0px;
-				width: 100%;
-			}
-
-			.info a {
-				color: #00ffff;
-			}
-		</style>
-	</head>
-	<body>
-		<script src="../build/three.js"></script>
-
-		<script src="js/controls/OrbitControls.js"></script>
-
-		<script src="js/postprocessing/EffectComposer.js"></script>
-		<script src="js/postprocessing/RenderPass.js"></script>
-		<script src="js/postprocessing/ShaderPass.js"></script>
-		<script src="js/postprocessing/HalftonePass.js"></script>
-
-		<script src="js/shaders/CopyShader.js"></script>
-		<script src="js/shaders/HalftoneShader.js"></script>
-		<script src="js/shaders/DepthLimitedBlurShader.js"></script>
-		<script src="js/shaders/UnpackDepthRGBAShader.js"></script>
-
-		<script src="js/Detector.js"></script>
-		<script src='js/libs/stats.min.js'></script>
-		<script src='js/libs/dat.gui.min.js'></script>
-
-		<div class="info">
-			<a href="http://threejs.org" target="_blank" rel="noopener noreferrer">three.js</a> - Halftone shader by
-			<a href="https://github.com/meatbags" target="_blank">@meatbags</a>
-		</div>
-
-		<script>
-
-		if (!Detector.webgl) {
-			Detector.addGetWebGLMessage();
-		}
-
-		class Example {
-			constructor() {
-				this.wrapper = document.createElement('div');
-				document.body.appendChild(this.wrapper);
-
-			 	this.clock = new THREE.Clock();
-			 	this.width = window.innerWidth;
-			 	this.height = window.innerHeight;
-			 	this.camera = new THREE.PerspectiveCamera(75, this.width / this.height, 1, 1000);
-				this.camera.position.z = 10;
-				this.rotationSpeed = Math.PI / 64;
-
-				this.initScene();
-				this.initRender();
-				this.initGUI();
-				this.loop();
-			}
-
-			initScene() {
-				var geo = new THREE.BoxBufferGeometry(2, 2, 2);
-				var mat = new THREE.ShaderMaterial({
-					uniforms: {},
-					vertexShader: `
-						varying vec2 vUV;
-						varying vec3 vNormal;
-
-						void main() {
-			      	vUV = uv;
-							vNormal = vec3(normal);
-			      	gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
-			    	}`,
-					fragmentShader: `
-						varying vec2 vUV;
-						varying vec3 vNormal;
-
-						void main() {
-							vec4 c = vec4(abs(vNormal) + vec3(vUV, 0.0), 0.0);
-							gl_FragColor = c;
-						}`
-				});
-				this.group = new THREE.Group();
-				var rand = (x) => { return x * Math.random() - x * 0.5; };
-
-				for (var i=0; i<50; ++i) {
-					var mesh = new THREE.Mesh(geo.clone(), mat.clone());
-					mesh.position.set(rand(15), rand(15), rand(15));
-					mesh.rotation.set(rand(Math.PI), rand(Math.PI), rand(Math.PI));
-					this.group.add(mesh);
-				}
-
-				var floor = new THREE.Mesh(new THREE.BoxBufferGeometry(100, 1, 100), new THREE.MeshPhongMaterial({}));
-				var light = new THREE.PointLight(0xffffff, 1.0, 50, 2);
-				light.position.y = 2;
-				floor.position.y = -10;
-				this.group.add(floor, light);
-
-				this.scene = new THREE.Scene();
-				this.scene.background = new THREE.Color(0x444444);
-				this.scene.add(this.group);
-			}
-
-			initRender() {
-				// 3js renderer
-				this.renderer = new THREE.WebGLRenderer({antialias: true});
-				this.renderer.setPixelRatio(window.devicePixelRatio);
-				this.renderer.setSize(window.innerWidth, window.innerHeight);
-
-				// effect composer
-				this.composer = new THREE.EffectComposer(this.renderer);
-			 	this.renderPass = new THREE.RenderPass(this.scene, this.camera);
-				this.halftonePass = new THREE.HalftonePass(this.width, this.height);
-				this.halftonePass.renderToScreen = true;
-				this.composer.addPass(this.renderPass);
-				this.composer.addPass(this.halftonePass);
-
-				window.onresize = () => {
-					this.width = window.innerWidth;
-					this.height = window.innerHeight;
-					this.renderer.setSize(this.width, this.height);
-					this.composer.setSize(this.width, this.height);
-					this.camera.aspect = this.width / this.height;
-					this.camera.updateProjectionMatrix();
-				}
-
-				// add to doc
-				this.wrapper.appendChild(this.renderer.domElement);
-			}
-
-			initGUI() {
-				var guiTarget = this.halftonePass.uniforms;
-				var controller = {
-					radius: guiTarget.radius.value,
-					rotateC: guiTarget.rC.value / (Math.PI / 180),
-					rotateM: guiTarget.rM.value / (Math.PI / 180),
-					rotateY: guiTarget.rY.value / (Math.PI / 180),
-					shape: guiTarget.shape.value,
-					greyscale: guiTarget.greyscale.value,
-					blending: guiTarget.blending.value,
-					blendingMode: guiTarget.blendingMode.value,
-					disable: guiTarget.disable.value
-				};
-				var onGUIChange = function() {
-					guiTarget.radius.value = controller.radius;
-					guiTarget.rC.value = controller.rotateC * (Math.PI / 180);
-					guiTarget.rM.value = controller.rotateM * (Math.PI / 180);
-					guiTarget.rY.value = controller.rotateY * (Math.PI / 180);
-					guiTarget.shape.value = controller.shape;
-					guiTarget.greyscale.value = controller.greyscale;
-					guiTarget.blending.value = controller.blending;
-					guiTarget.blendingMode.value = controller.blendingMode;
-					guiTarget.disable.value = controller.disable;
-				};
-				this.gui = new dat.GUI();
-				this.gui.add(controller, 'shape', {'dot': 1, 'euclidean-dot': 2, 'ellipse': 3, 'line': 4, 'square': 5}).onChange(onGUIChange);
-				this.gui.add(controller, 'radius', 1, 50).onChange(onGUIChange);
-				this.gui.add(controller, 'rotateC', 0, 90).onChange(onGUIChange);
-				this.gui.add(controller, 'rotateM', 0, 90).onChange(onGUIChange);
-				this.gui.add(controller, 'rotateY', 0, 90).onChange(onGUIChange);
-				this.gui.add(controller, 'greyscale').onChange(onGUIChange);
-				this.gui.add(controller, 'blending', 0, 1, 0.01).onChange(onGUIChange);
-				this.gui.add(controller, 'blendingMode', {'normal': 1, 'add': 2, 'multiply': 3, 'lighter': 4, 'darker': 5}).onChange(onGUIChange);
-				this.gui.add(controller, 'disable').onChange(onGUIChange);
-
-				this.stats = new Stats();
-				this.wrapper.appendChild( this.stats.dom );
-
-				var controls = new THREE.OrbitControls( this.camera, this.renderer.domElement );
-				controls.target.set( 0, 0, 0 );
-				controls.update();
-
-				// reset
-				onGUIChange();
-			}
-
-			loop() {
-				requestAnimationFrame(() => { this.loop(); });
-				var delta = this.clock.getDelta();
-				this.group.rotation.y += delta * this.rotationSpeed;
-				this.composer.render(delta);
-				this.stats.update();
-			}
-		}
-
-		var example = new Example();
-
-		</script>
-	</body>
-</html>

+ 216 - 0
examples/webgl_postprocessing_rgb_halftone.html

@@ -0,0 +1,216 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<style>
+			body {
+				font-family: Monospace;
+				background-color: #f0f0f0;
+				margin: 0px;
+				overflow: hidden;
+			}
+
+			.info {
+				position: absolute;
+				background-color: black;
+				opacity: 0.8;
+				color: white;
+				text-align: center;
+				top: 0px;
+				width: 100%;
+				padding: 2px;
+			}
+
+			.info a {
+				color: #00ffff;
+			}
+		</style>
+	</head>
+	<body>
+		<script src="../build/three.js"></script>
+
+		<script src="js/postprocessing/EffectComposer.js"></script>
+		<script src="js/postprocessing/RenderPass.js"></script>
+		<script src="js/postprocessing/ShaderPass.js"></script>
+		<script src="js/postprocessing/HalftonePass.js"></script>
+
+		<script src="js/shaders/CopyShader.js"></script>
+		<script src="js/shaders/HalftoneShader.js"></script>
+		<script src="js/shaders/DepthLimitedBlurShader.js"></script>
+		<script src="js/shaders/UnpackDepthRGBAShader.js"></script>
+
+		<script src="js/controls/OrbitControls.js"></script>
+		<script src="js/Detector.js"></script>
+		<script src='js/libs/stats.min.js'></script>
+		<script src='js/libs/dat.gui.min.js'></script>
+
+		<div class="info">
+			<a href="http://threejs.org" target="_blank" rel="noopener noreferrer">three.js</a> - RGB Halftone post-processing by
+			<a href="https://github.com/meatbags" target="_blank">Xavier Burrow</a>
+		</div>
+
+		<script>
+
+		if ( ! Detector.webgl ) {
+
+			Detector.addGetWebGLMessage();
+
+		}
+
+		// setup
+		var wrapper, renderer, clock, camera, controls, stats;
+
+		wrapper = document.createElement( 'div' );
+		renderer = new THREE.WebGLRenderer();
+		renderer.setPixelRatio( window.devicePixelRatio );
+		renderer.setSize( window.innerWidth, window.innerHeight );
+		clock = new THREE.Clock();
+		camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 1000 );
+		camera.position.z = 12;
+		rotationSpeed = Math.PI / 64;
+		stats = new Stats();
+		wrapper.appendChild( renderer.domElement );
+		wrapper.appendChild( stats.dom );
+		document.body.appendChild( wrapper );
+
+		// camera controls
+		controls = new THREE.OrbitControls( camera, renderer.domElement );
+		controls.target.set( 0, 0, 0 );
+		controls.update();
+
+		// scene
+		var scene, group, mat, floor, light;
+
+		scene = new THREE.Scene();
+		scene.background = new THREE.Color( 0x444444 );
+		group = new THREE.Group();
+		floor = new THREE.Mesh( new THREE.BoxBufferGeometry( 100, 1, 100 ), new THREE.MeshPhongMaterial( {} ) );
+		floor.position.y = - 10;
+		light = new THREE.PointLight( 0xffffff, 1.0, 50, 2 );
+		light.position.y = 2;
+		group.add( floor, light );
+		scene.add( group );
+
+		mat = new THREE.ShaderMaterial( {
+
+			uniforms: {},
+
+			vertexShader: [
+				"varying vec2 vUV;",
+				"varying vec3 vNormal;",
+
+				"void main() {",
+
+					"vUV = uv;",
+					"vNormal = vec3( normal );",
+					"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+
+				"}"
+			].join("\n"),
+
+			fragmentShader: [
+				"varying vec2 vUV;",
+				"varying vec3 vNormal;",
+
+				"void main() {",
+
+					"vec4 c = vec4( abs( vNormal ) + vec3( vUV, 0.0 ), 0.0 );",
+					"gl_FragColor = c;",
+
+				"}"
+			].join("\n")
+		} );
+
+		for ( var i = 0; i < 50; ++ i ) {
+
+			// fill scene with coloured cubes
+			var mesh = new THREE.Mesh( new THREE.BoxBufferGeometry( 2, 2, 2 ), mat );
+			mesh.position.set( Math.random() * 16 - 8, Math.random() * 16 - 8, Math.random() * 16 - 8 );
+			mesh.rotation.set( Math.random() * Math.PI * 2, Math.random() * Math.PI * 2, Math.random() * Math.PI * 2 );
+			group.add( mesh );
+
+		}
+
+		// post-processing
+		var composer, renderPass, halftonePass;
+
+		composer = new THREE.EffectComposer( renderer );
+		renderPass = new THREE.RenderPass( scene, camera );
+		halftonePass = new THREE.HalftonePass( window.innerWidth, window.innerHeight );
+		halftonePass.renderToScreen = true;
+		composer.addPass( renderPass );
+		composer.addPass( halftonePass );
+
+		window.onresize = function () {
+
+			// resize composer
+			renderer.setSize( window.innerWidth, window.innerHeight );
+			composer.setSize( window.innerWidth, window.innerHeight );
+			camera.aspect = window.innerWidth / window.innerHeight;
+			camera.updateProjectionMatrix();
+
+		};
+
+		// GUI
+		var controller, gui, onGUIChange;
+
+		controller = {
+			radius: halftonePass.uniforms.radius.value,
+			rotateR: halftonePass.uniforms.rotateR.value / ( Math.PI / 180 ),
+			rotateG: halftonePass.uniforms.rotateG.value / ( Math.PI / 180 ),
+			rotateB: halftonePass.uniforms.rotateB.value / ( Math.PI / 180 ),
+			scatter: halftonePass.uniforms.scatter.value,
+			shape: halftonePass.uniforms.shape.value,
+			greyscale: halftonePass.uniforms.greyscale.value,
+			blending: halftonePass.uniforms.blending.value,
+			blendingMode: halftonePass.uniforms.blendingMode.value,
+			disable: halftonePass.uniforms.disable.value
+		};
+
+		onGUIChange = function () {
+
+			// update uniforms
+			halftonePass.uniforms.radius.value = controller.radius;
+			halftonePass.uniforms.rotateR.value = controller.rotateR * ( Math.PI / 180 );
+			halftonePass.uniforms.rotateG.value = controller.rotateG * ( Math.PI / 180 );
+			halftonePass.uniforms.rotateB.value = controller.rotateB * ( Math.PI / 180 );
+			halftonePass.uniforms.scatter.value = controller.scatter;
+			halftonePass.uniforms.shape.value = controller.shape;
+			halftonePass.uniforms.greyscale.value = controller.greyscale;
+			halftonePass.uniforms.blending.value = controller.blending;
+			halftonePass.uniforms.blendingMode.value = controller.blendingMode;
+			halftonePass.uniforms.disable.value = controller.disable;
+
+		};
+
+		gui = new dat.GUI();
+		gui.add( controller, 'shape', { 'Dot': 1, 'Ellipse': 2, 'Line': 3, 'Square': 4 } ).onChange( onGUIChange );
+		gui.add( controller, 'radius', 1, 25 ).onChange( onGUIChange );
+		gui.add( controller, 'rotateR', 0, 90 ).onChange( onGUIChange );
+		gui.add( controller, 'rotateG', 0, 90 ).onChange( onGUIChange );
+		gui.add( controller, 'rotateB', 0, 90 ).onChange( onGUIChange );
+		gui.add( controller, 'scatter', 0, 1, 0.01 ).onChange( onGUIChange );
+		gui.add( controller, 'greyscale' ).onChange( onGUIChange );
+		gui.add( controller, 'blending', 0, 1, 0.01 ).onChange( onGUIChange );
+		gui.add( controller, 'blendingMode', { 'Linear': 1, 'Multiply': 2, 'Add': 3, 'Lighter': 4, 'Darker': 5 } ).onChange( onGUIChange );
+		gui.add( controller, 'disable' ).onChange( onGUIChange );
+
+		function loop() {
+
+			// demo loop
+			var delta = clock.getDelta();
+			requestAnimationFrame( loop );
+			stats.update();
+			group.rotation.y += delta * rotationSpeed;
+			composer.render( delta );
+
+		}
+
+		loop();
+
+
+		</script>
+	</body>
+</html>