2
0
Эх сурвалжийг харах

nearest grid point processing

meatbags 7 жил өмнө
parent
commit
32dde5558e

+ 140 - 12
examples/js/shaders/HalftoneShader.js

@@ -2,46 +2,174 @@
  * @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)
  */
 
 THREE.HalftoneShader = {
 	uniforms: {
+		"shape": {value: 1},
 		"tDiffuse": {value: null},
 		"radius": {value: 5},
 		"rC": {value: Math.PI * 0.25},
 		"rM": {value: Math.PI * 0.33},
 		"rY": {value: Math.PI * 0.66},
-		"shape": {value: 1},
 		"width": {value: 1},
-		"height": {value: 1}
+		"height": {value: 1},
+		"blending": {value: 0},
+		"blendingMode": {value: 1},
+		"greyscale": {value: false},
+		"disable": {value: false}
 	},
 	vertexShader: `
-    varying vec2 vUv;
+    varying vec2 vUV;
 
     void main() {
-      vUv = uv;
+      vUV = uv;
       gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
     }`,
 	fragmentShader: `
-		varying vec2 vUv;
+		#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 rY;
 		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 rand(vec2 seed){
-    	return fract(sin(dot(seed.xy, vec2(12.9898,78.233))) * 43758.5453);
+		float blend(float a, float b, float t) {
+			return a * (1.0 - t) + b * t;
 		}
 
-		vec2 gridReference(vec2 coord, float step, float rotation) {
-			return vec2(0, 0);
+		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 theta = atan(p.y - coord.y, p.x - coord.x);
+			float sin_t = abs(sin(theta));
+			float cos_t = abs(cos(theta));
+
+			if (shape == 2) {
+				// euclidean dot
+				float square = r + ((sin_t > cos_t) ? r - sin_t * r : r - cos_t * r);
+				if (r <= 0.5) {
+					r = blend(r, square, r * 2.0);
+				} else {
+					float max_r = r + 2.0 * ((sin_t > cos_t) ? r - sin_t * r : r - cos_t * r);
+					r = blend(square, max_r, pow(abs((r - 0.5) * 2.0), 0.4));
+				}
+			} else if (shape == 5) {
+				// 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, float aspect) {
+			// 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 * aspect - n.x * line_scale
+			);
 		}
 
 		void main() {
-			vec4 color = texture2D(tDiffuse, vUv);
-			gl_FragColor = color;
+			if (!disable) {
+				vec2 origin = vec2(0, 0);
+				float step = radius / width;
+				float radius_max = step;
+				float aspect = height / width;
+				float aa = (step < 1.0 / width) ? step * 0.5 : 1.0 / width;
+				vec2 normC = vec2(cos(rC), sin(rC));
+				vec2 normM = vec2(cos(rM), sin(rM));
+				vec2 normY = vec2(cos(rY), sin(rY));
+
+				// sampling
+				vec2 coordC = gridReference(vUV, origin, normC, step, aspect);
+				vec2 coordM = gridReference(vUV, origin, normM, step, aspect);
+				vec2 coordY = gridReference(vUV, origin, normY, step, aspect);
+				float distC = shapeRadius(texture2D(tDiffuse, coordC).r, vUV, coordC) * radius_max - shapeDistance(vUV, coordC, normC);
+				float distM = shapeRadius(texture2D(tDiffuse, coordM).g, vUV, coordM) * radius_max - shapeDistance(vUV, coordM, normM);
+				float distY = shapeRadius(texture2D(tDiffuse, coordY).b, vUV, coordY) * radius_max - shapeDistance(vUV, coordY, normY);
+				float r = (distC > 0.0) ? clamp(0.0, 1.0, distC / aa) : 0.0;
+				float g = (distM > 0.0) ? clamp(0.0, 1.0, distM / aa) : 0.0;
+				float b = (distY > 0.0) ? clamp(0.0, 1.0, distY / aa) : 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);
+			}
 		}`
 };

+ 50 - 13
examples/webgl_postprocessing_color_halftone.html

@@ -29,15 +29,21 @@
 	</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">
@@ -60,7 +66,7 @@
 			 	this.width = window.innerWidth;
 			 	this.height = window.innerHeight;
 			 	this.camera = new THREE.PerspectiveCamera(75, this.width / this.height, 1, 1000);
-				this.camera.position.z = 20;
+				this.camera.position.z = 10;
 				this.rotationSpeed = Math.PI / 64;
 
 				this.initScene();
@@ -91,7 +97,6 @@
 							gl_FragColor = c;
 						}`
 				});
-
 				this.group = new THREE.Group();
 				var rand = (x) => { return x * Math.random() - x * 0.5; };
 
@@ -102,8 +107,14 @@
 					this.group.add(mesh);
 				}
 
+				var floor = new THREE.Mesh(new THREE.BoxBufferGeometry(50, 1, 50), 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(0x111111);
+				this.scene.background = new THREE.Color(0x444444);
 				this.scene.add(this.group);
 			}
 
@@ -121,6 +132,15 @@
 				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);
 			}
@@ -128,28 +148,44 @@
 			initGUI() {
 				var guiTarget = this.halftonePass.uniforms;
 				var controller = {
-					radius: 5,
-					rotateSampleGrid: 45,
+					radius: 10,
 					rotateC: 45,
-					rotateM: 30,
+					rotateM: 75,
 					rotateY: 60,
-					shape: 1
+					shape: 1,
+					greyscale: false,
+					blending: 0,
+					blendingMode: 1,
+					disable: false
 				};
 				var onGUIChange = function() {
 					guiTarget.radius.value = controller.radius;
-					guiTarget.rSample.value = controller.rotateSampleGrid * (Math.PI / 180);
 					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}).onChange(onGUIChange);
+				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, 'rotateSampleGrid', 0, 360).onChange(onGUIChange);
-				this.gui.add(controller, 'rotateC', 0, 360).onChange(onGUIChange);
-				this.gui.add(controller, 'rotateM', 0, 360).onChange(onGUIChange);
-				this.gui.add(controller, 'rotateY', 0, 360).onChange(onGUIChange);
+				this.gui.add(controller, 'rotateC', 0, 180).onChange(onGUIChange);
+				this.gui.add(controller, 'rotateM', 0, 180).onChange(onGUIChange);
+				this.gui.add(controller, 'rotateY', 0, 180).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();
@@ -160,6 +196,7 @@
 				var delta = this.clock.getDelta();
 				this.group.rotation.y += delta * this.rotationSpeed;
 				this.composer.render(delta);
+				this.stats.update();
 			}
 		}