Permalink
Browse files

Extended map display shader mechanism.

- Inverted shader call logic
	- Instead of mapDisplay calling shader() on itself, the mapDisplay is
passed to the Shader
	- Allows more complex shading behaviour, e.g. a two-pass Gaussian Blur.

- Created super class MapDisplayShader

- Two shade() methods can be implemented, shadeWithMarker and
shadeWithoutMarker
	- Which are called at different positions in postDraw() from
mapDisplay.

- Enhanced MaskedMapDisplayShader, now has two options:
	a) use dynamic mask canvas
	b) use mask image

- Added Blur and Deform shader

- Added shading examples
  • Loading branch information...
1 parent 80afc1a commit 4aca4b0ca033ea0cc0499148c5342be8c430696c @tillnagel committed Aug 10, 2013
View
@@ -0,0 +1,59 @@
+// Adapted from:
+// http://callumhay.blogspot.com/2010/09/gaussian-blur-shader-glsl.html
+
+#ifdef GL_ES
+precision mediump float;
+precision mediump int;
+#endif
+
+#define PROCESSING_TEXTURE_SHADER
+
+uniform sampler2D texture;
+
+// The inverse of the texture dimensions along X and Y
+uniform vec2 texOffset;
+
+varying vec4 vertColor;
+varying vec4 vertTexCoord;
+
+uniform int blurSize;
+uniform int horizontalPass; // 0 or 1 to indicate vertical or horizontal pass
+uniform float sigma; // The sigma value for the gaussian function: higher value means more blur
+ // A good value for 9x9 is around 3 to 5
+ // A good value for 7x7 is around 2.5 to 4
+ // A good value for 5x5 is around 2 to 3.5
+ // ... play around with this based on what you need :)
+
+const float pi = 3.14159265;
+
+void main() {
+ float numBlurPixelsPerSide = float(blurSize / 2);
+
+ vec2 blurMultiplyVec = 0 < horizontalPass ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
+
+ // Incremental Gaussian Coefficent Calculation (See GPU Gems 3 pp. 877 - 889)
+ vec3 incrementalGaussian;
+ incrementalGaussian.x = 1.0 / (sqrt(2.0 * pi) * sigma);
+ incrementalGaussian.y = exp(-0.5 / (sigma * sigma));
+ incrementalGaussian.z = incrementalGaussian.y * incrementalGaussian.y;
+
+ vec4 avgValue = vec4(0.0, 0.0, 0.0, 0.0);
+ float coefficientSum = 0.0;
+
+ // Take the central sample first...
+ avgValue += texture2D(texture, vertTexCoord.st) * incrementalGaussian.x;
+ coefficientSum += incrementalGaussian.x;
+ incrementalGaussian.xy *= incrementalGaussian.yz;
+
+ // Go through the remaining 8 vertical samples (4 on each side of the center)
+ for (float i = 1.0; i <= numBlurPixelsPerSide; i++) {
+ avgValue += texture2D(texture, vertTexCoord.st - i * texOffset *
+ blurMultiplyVec) * incrementalGaussian.x;
+ avgValue += texture2D(texture, vertTexCoord.st + i * texOffset *
+ blurMultiplyVec) * incrementalGaussian.x;
+ coefficientSum += 2.0 * incrementalGaussian.x;
+ incrementalGaussian.xy *= incrementalGaussian.yz;
+ }
+
+ gl_FragColor = avgValue / coefficientSum;
+}
View
@@ -0,0 +1,31 @@
+#ifdef GL_ES
+precision mediump float;
+precision mediump int;
+#endif
+
+#define PROCESSING_TEXTURE_SHADER
+
+uniform sampler2D texture;
+
+uniform float time;
+uniform vec2 resolution;
+uniform vec2 mouse;
+
+void main(void) {
+ vec2 p = -1.0 + 2.0 * gl_FragCoord.xy / resolution.xy;
+ vec2 m = -1.0 + 2.0 * mouse.xy / resolution.xy;
+
+ float a1 = atan(p.y - m.y, p.x - m.x);
+ float r1 = sqrt(dot(p - m, p - m));
+ float a2 = atan(p.y + m.y, p.x + m.x);
+ float r2 = sqrt(dot(p + m, p + m));
+
+ vec2 uv;
+ uv.x = 0.2 * time + (r1 - r2) * 0.25;
+ uv.y = sin(2.0 * (a1 - a2));
+
+ float w = r1 * r2 * 0.8;
+ vec3 col = texture2D(texture, 0.5 - 0.495 * uv).xyz;
+
+ gl_FragColor = vec4(col / (0.1 + w), 1.0);
+}
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
@@ -3,6 +3,7 @@
import processing.core.PApplet;
import processing.core.PConstants;
import processing.core.PGraphics;
+import de.fhpotsdam.unfolding.mapdisplay.shaders.MapDisplayShader;
import de.fhpotsdam.unfolding.mapdisplay.shaders.MaskedMapDisplayShader;
import de.fhpotsdam.unfolding.marker.Marker;
import de.fhpotsdam.unfolding.marker.MarkerManager;
@@ -16,31 +17,33 @@
protected PGraphics offscreenCutoffPG;
protected float opacity = 255;
-
- protected MaskedMapDisplayShader mapDisplayShader = null;
+
+ protected MapDisplayShader mapDisplayShader = null;
public OpenGLMapDisplay(PApplet papplet, AbstractMapProvider provider, float offsetX, float offsetY,
float width, float height) {
super(papplet, provider, offsetX, offsetY, width, height);
- offscreenPG = papplet.createGraphics((int) width, (int) height,OPENGL);
- offscreenCutoffPG = papplet.createGraphics((int) width, (int) height,OPENGL);
+ offscreenPG = papplet.createGraphics((int) width, (int) height, OPENGL);
+ offscreenCutoffPG = papplet.createGraphics((int) width, (int) height, OPENGL);
}
-
- public void setMapDisplayShader(MaskedMapDisplayShader shader){
+
+ public void setMapDisplayShader(MapDisplayShader shader) {
this.mapDisplayShader = shader;
}
-
- public MaskedMapDisplayShader getMapDisplayShader(){
+
+ public MapDisplayShader getMapDisplayShader() {
return mapDisplayShader;
}
@Override
public void resize(float width, float height) {
super.resize(width, height);
- offscreenPG = papplet.createGraphics((int) width, (int) height,OPENGL);
- offscreenCutoffPG = papplet.createGraphics((int) width, (int) height,OPENGL);
- if(mapDisplayShader != null){
+
+ offscreenPG = papplet.createGraphics((int) width, (int) height, OPENGL);
+ offscreenCutoffPG = papplet.createGraphics((int) width, (int) height, OPENGL);
+
+ if (mapDisplayShader != null) {
mapDisplayShader.resize(width, height);
}
}
@@ -54,11 +57,17 @@ public PGraphics getInnerPG() {
public PGraphics getOuterPG() {
return offscreenCutoffPG;
}
-
+
@Override
protected void postDraw() {
// Draws inner map (with inner marker) and outer marker
offscreenCutoffPG.beginDraw();
+ // REVISIT map background color
+ offscreenCutoffPG.background(0);
+ if (mapDisplayShader != null) {
+ // NB: Uses offscreenPG (and not offscreenCutofPG) to not get 'Shader must be COLOR type' error
+ mapDisplayShader.shadeWithoutMarkers(offscreenPG);
+ }
offscreenCutoffPG.image(offscreenPG, 0, 0);
for (MarkerManager<Marker> mm : markerManagerList) {
mm.draw();
@@ -71,10 +80,11 @@ protected void postDraw() {
canvasPG.pushMatrix();
canvasPG.translate(offsetX, offsetY);
canvasPG.applyMatrix(matrix);
- if(mapDisplayShader != null){
- canvasPG.shader(mapDisplayShader.getShader());
+ if (mapDisplayShader != null) {
+ mapDisplayShader.shadeWithMarkers(canvasPG);
}
canvasPG.image(offscreenCutoffPG, 0, 0);
canvasPG.popMatrix();
+ canvasPG.resetShader();
}
}
@@ -0,0 +1,39 @@
+package de.fhpotsdam.unfolding.mapdisplay.shaders;
+
+import processing.core.PApplet;
+import processing.core.PGraphics;
+
+/**
+ * This shader blurs a map.
+ *
+ * Implements shade without markers, i.e. blurs only the map but not the markers.
+ * Uses two-pass, i.e. calls PGraphics.shader(blurShader) twice.
+ *
+ */
+public class BlurredMapDisplayShader extends MapDisplayShader {
+
+ private static final int DEFAULT_BLUR_SIZE = 9;
+ private static final float DEFAULT_SIGMA = 5.0f;
+
+ public BlurredMapDisplayShader(PApplet p, int blurSize, float sigma) {
+ super(p);
+ shader = p.loadShader("test/blur.glsl");
+ shader.set("blurSize", blurSize);
+ shader.set("sigma", sigma);
+ }
+
+ public BlurredMapDisplayShader(PApplet p) {
+ this(p, DEFAULT_BLUR_SIZE, DEFAULT_SIGMA);
+ }
+
+ @Override
+ public void shadeWithoutMarkers(PGraphics mapDisplayCanvas) {
+ // Two-pass Gaussian blur, first vertical, then horizontal
+
+ shader.set("horizontalPass", 0);
+ mapDisplayCanvas.shader(shader);
+ shader.set("horizontalPass", 1);
+ mapDisplayCanvas.shader(shader);
+ }
+
+}
@@ -0,0 +1,47 @@
+package de.fhpotsdam.unfolding.mapdisplay.shaders;
+
+import processing.core.PApplet;
+import processing.core.PGraphics;
+import processing.opengl.PShader;
+
+public class MapDisplayShader {
+
+ protected PApplet p;
+
+ protected PShader shader;
+
+ public MapDisplayShader(PApplet p) {
+ this.p = p;
+ }
+
+ public PShader getShader() {
+ return shader;
+ }
+
+ /**
+ * Resizes the shader. Optional, only implement if needed.
+ *
+ * @param width
+ * The new width of the canvas.
+ * @param height
+ * The new height of the canvas.
+ */
+ public void resize(float width, float height) {
+
+ }
+
+ /**
+ * Applies the shader to the map display canvas.
+ *
+ * @param innerCanvas
+ * The inner canvas.
+ */
+ public void shadeWithoutMarkers(PGraphics innerCanvas) {
+
+ }
+
+ public void shadeWithMarkers(PGraphics innerCanvas) {
+
+ }
+
+}
@@ -2,31 +2,73 @@
import processing.core.PApplet;
import processing.core.PGraphics;
-import processing.opengl.PShader;
-
-public class MaskedMapDisplayShader{
-
- protected PGraphics maskImage;
- protected PShader shader;
-
- public MaskedMapDisplayShader(PApplet p, float width,float height){
-
- maskImage = p.createGraphics((int)width, (int)height,PApplet.OPENGL);
- maskImage.noSmooth();
-
+import processing.core.PImage;
+
+/**
+ * This shader masks a map. The offscreen graphic canvas as mask
+ *
+ *
+ */
+public class MaskedMapDisplayShader extends MapDisplayShader {
+
+ /** The mask canvas. */
+ protected PGraphics mask;
+
+ /**
+ * Creates a MaskedMapDisplayShader to draw the mask dynamically. Call {@link #getMask()} to get the mask canvas to
+ * draw on.
+ *
+ * @param p
+ * The PApplet.
+ * @param width
+ * The width of the mask.
+ * @param height
+ * The height of the mask.
+ */
+ public MaskedMapDisplayShader(PApplet p, float width, float height) {
+ super(p);
+ mask = p.createGraphics((int) width, (int) height, PApplet.OPENGL);
+ mask.noSmooth();
+
shader = p.loadShader("test/mask.glsl");
- shader.set("mask", maskImage);
+ shader.set("mask", mask);
+ }
+
+ /**
+ * Creates a MaskedMapDisplayShader with the given mask image.
+ *
+ * @param p
+ * The PApplet.
+ * @param width
+ * The width of the mask.
+ * @param height
+ * The height of the mask.
+ * @param maskImage
+ * An image to be used as mask.
+ */
+ public MaskedMapDisplayShader(PApplet p, float width, float height, PImage maskImage) {
+ this(p, width, height);
+ mask.beginDraw();
+ mask.image(maskImage, 0, 0);
+ mask.endDraw();
}
-
- public void resize(float width,float height){
- maskImage.resize((int)width, (int)height);
+
+ public void resize(float width, float height) {
+ mask.resize((int) width, (int) height);
}
-
+
+ /**
+ * Returns the mask canvas to dynamically create or update the mask.
+ *
+ * @return The mask.
+ */
public PGraphics getMask() {
- return maskImage;
+ return mask;
}
-
- public PShader getShader(){
- return shader;
+
+ @Override
+ public void shadeWithMarkers(PGraphics mapDisplayCanvas) {
+ mapDisplayCanvas.shader(getShader());
}
+
}
Oops, something went wrong.

0 comments on commit 4aca4b0

Please sign in to comment.