diff --git a/vtm-android-example/src/org/oscim/android/test/S3DBMapActivity.java b/vtm-android-example/src/org/oscim/android/test/S3DBMapActivity.java
index 2f655620..5ee41e64 100644
--- a/vtm-android-example/src/org/oscim/android/test/S3DBMapActivity.java
+++ b/vtm-android-example/src/org/oscim/android/test/S3DBMapActivity.java
@@ -1,8 +1,8 @@
 package org.oscim.android.test;
 
 import org.oscim.android.cache.TileCache;
+import org.oscim.layers.tile.TileLayer;
 import org.oscim.layers.tile.s3db.S3DBLayer;
-import org.oscim.layers.tile.vector.labeling.LabelLayer;
 import org.oscim.theme.VtmThemes;
 import org.oscim.tiling.TileSource;
 import org.oscim.tiling.source.oscimap4.OSciMap4TileSource;
@@ -18,6 +18,7 @@ public class S3DBMapActivity extends BaseMapActivity {
 		super.onCreate(savedInstanceState);
 
 		mMap.setTheme(VtmThemes.DEFAULT);
+
 		//mMap.setTheme(VtmThemes.TRONRENDER);
 		//mMap.setTheme(VtmThemes.OSMARENDER);
 
@@ -28,9 +29,16 @@ public class S3DBMapActivity extends BaseMapActivity {
 			mS3dbCache.setCacheSize(512 * (1 << 10));
 			ts.setCache(mS3dbCache);
 		}
+		TileLayer tl = new S3DBLayer(mMap, ts);
+		//OffscreenRenderer or = new OffscreenRenderer(mMap.getWidth(),
+		//                                             mMap.getHeight());
+		//or.setRenderer(tl.getRenderer());
+		mMap.layers().add(tl);
 
-		mMap.layers().add(new S3DBLayer(mMap, ts));
-		mMap.layers().add(new LabelLayer(mMap, mBaseLayer));
+		//mMap.layers().add(new GenericLayer(mMap, or));
+
+		//mMap.layers().add(new LabelLayer(mMap, mBaseLayer));
+		mMap.setMapPosition(53.08, 8.83, Math.pow(2, 17));
 	}
 
 	@Override
diff --git a/vtm/resources/assets/shaders/post_combined.glsl b/vtm/resources/assets/shaders/post_combined.glsl
new file mode 100644
index 00000000..395ae666
--- /dev/null
+++ b/vtm/resources/assets/shaders/post_combined.glsl
@@ -0,0 +1,187 @@
+#ifdef GLES
+precision highp float;
+#endif
+uniform vec2 u_pixel;
+attribute vec4 a_pos;
+varying vec2 tex_pos;
+varying vec2 tex_nw;
+varying vec2 tex_ne;
+varying vec2 tex_sw;
+varying vec2 tex_se;
+void
+main()
+{
+  gl_Position = a_pos;
+  tex_pos = (a_pos.xy + 1.0) * 0.5;
+  vec2 pixel = u_pixel * 2.5;
+  tex_nw = tex_pos + vec2(-1.0, -1.0) * pixel;
+  tex_ne = tex_pos + vec2(1.0, -1.0) * pixel;
+  tex_sw = tex_pos + vec2(-1.0, 1.0) * pixel;
+  tex_se = tex_pos + vec2(1.0, 1.0) * pixel;
+}
+
+$$
+
+#ifdef GLES
+precision highp float;
+#endif
+uniform sampler2D u_texColor;
+uniform sampler2D u_tex;
+uniform vec2 u_pixel;
+varying vec2 tex_pos;
+varying vec2 tex_nw;
+varying vec2 tex_ne;
+varying vec2 tex_sw;
+varying vec2 tex_se;
+
+const vec2 m_SpanMax = vec2(2.5, 2.5);
+const float m_ReduceMul = 0.15;
+const vec3 luma = vec3(0.299, 0.587, 0.114);
+
+#define FXAA_REDUCE_MIN   (1.0/128.0)
+
+// gauss bell center
+const float gdisplace = 0.2;
+const float nearZ = 1.0;
+const float farZ = 8.0;
+const int iterations = 2;
+
+float getDepth(float posZ) {
+  return (2.0 * nearZ) / (nearZ + farZ - posZ * (farZ - nearZ));
+}
+float compareDepths(in float depth1, in float depth2, inout float far) {
+  //   depth difference (0-100)
+  float diff = (depth1 - depth2) * 100.0;
+  //   set 'far == 1.0' when 'diff' > 'gdisplace'
+  far = step(diff, gdisplace);
+  // gauss bell width 2,
+  // if far reduce left bell width to avoid self-shadowing
+  float garea = max((1.0 - far) * 2.0, 0.1);
+
+  //return (step(diff, 0.0) * -0.1) + pow(2.7182, -2.0 * pow(diff - gdisplace,2.0) / pow(garea, 2.0));
+  return  pow(2.7182, -2.0 * pow(diff - gdisplace,2.0) / pow(garea, 2.0));
+}
+
+void
+addAO(int run, inout float depth, in float x1, in float y1, in float x2, in float y2, inout float ao){
+  float z_11 = getDepth(texture2D(u_tex, vec2(x1, y1)).x);
+  float z_12 = getDepth(texture2D(u_tex, vec2(x1, y2)).x);
+  float z_21 = getDepth(texture2D(u_tex, vec2(x2, y1)).x);
+  float z_22 = getDepth(texture2D(u_tex, vec2(x2, y2)).x);
+
+  if (run == 0)
+    depth = 0.5 * depth + (z_11 + z_12 + z_21 + z_22) * (0.25 * 0.5);
+
+  float f_11;
+  float d_11 = compareDepths(depth, z_11, f_11);
+
+  float f_12;
+  float d_12 = compareDepths(depth, z_12, f_12);
+
+  float f_21;
+  float d_21 = compareDepths(depth, z_21, f_21);
+
+  float f_22;
+  float d_22 = compareDepths(depth, z_22, f_22);
+
+  ao += 1.0 //(1.0 - step(1.0, x1)) * (1.0 - step(1.0, y1))
+      * (d_11 + f_11 * (1.0 - d_11) * d_22);
+
+  ao += 1.0 //(1.0 - step(1.0, x1)) * step(0.0, y2)
+      * (d_12 + f_12 * (1.0 - d_12) * d_21);
+
+  ao += 1.0 //step(0.0, x2) * (1.0 - step(1.0, y1))
+      * (d_21 + f_21 * (1.0 - d_21) * d_12);
+
+  ao += 1.0 //step(0.0, x2) * step(0.0, y2)
+      * (d_22 + f_22 * (1.0 - d_22) * d_11);
+}
+
+
+void
+main(){
+  vec2 pixel = u_pixel * 3.15;
+
+  vec4 rgbNW = texture2D(u_texColor, tex_nw);
+  vec4 rgbNE = texture2D(u_texColor, tex_ne);
+  vec4 rgbSW = texture2D(u_texColor, tex_sw);
+  vec4 rgbSE = texture2D(u_texColor, tex_se);
+
+  vec4 rgbM = texture2D(u_texColor, tex_pos);
+
+  if (rgbNW.a + rgbNE.a + rgbSW.a + rgbSE.a < 0.1) {
+    gl_FragColor = rgbM;
+    return;
+  }
+  //return vec4(rgbM - (rgbNW + rgbNE)*0.25,1.0);
+
+  float lumaNW = dot(rgbNW.rgb, luma);
+  float lumaNE = dot(rgbNE.rgb, luma);
+  float lumaSW = dot(rgbSW.rgb, luma);
+  float lumaSE = dot(rgbSE.rgb, luma);
+  float lumaM = dot(rgbM.rgb, luma);
+
+  float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));
+  float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));
+
+  //vec4 rgb = texture2D (tex, tex_pos);
+  //return vec4(0.5 + lumaM - lumaMin, lumaM, 0.5 + lumaM - lumaMax, 1.0) * rgb.a;
+
+  vec2 dir;
+  dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));
+  dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));
+
+  float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * m_ReduceMul), FXAA_REDUCE_MIN);
+
+  float rcpDirMin = 1.0 / (dirReduce + min(abs(dir.x), abs(dir.y)));
+
+  dir = min(m_SpanMax, max(-m_SpanMax, dir * rcpDirMin)) * pixel;
+
+  vec4 rgbA = 0.5 * (texture2D(u_texColor, tex_pos + dir * vec2(1.0 / 3.0 - 0.5))
+      + texture2D(u_texColor, tex_pos + dir * vec2(2.0 / 3.0 - 0.5)));
+
+  vec4 rgbB = rgbA * 0.5 + 0.25 * (texture2D(u_texColor, tex_pos + dir * vec2(0.0 / 3.0 - 0.5))
+      + texture2D(u_texColor, tex_pos + dir * vec2(3.0 / 3.0 - 0.5)));
+
+  float lumaB = dot(rgbB.rgb, luma.rgb);
+
+  float d = step(lumaB, lumaMin) + step(lumaMax, lumaB);
+
+  vec4 color = (1.0 - d) * rgbB + d * rgbA;
+
+  //vec4 color = texture2D(u_texColor, tex_pos);
+
+  float depth = texture2D(u_tex, tex_pos).x;
+  float foggy = pow(depth,3.0);
+
+  depth = getDepth(depth);
+
+  float x = tex_pos.x;
+  float y = tex_pos.y;
+  float pw = u_pixel.x;
+  float ph = u_pixel.y;
+  float ao = 0.0;
+  for (int i = 0; i < iterations; i++) {
+    float pwByDepth = pw / depth;
+    float phByDepth = ph / depth;
+    addAO(i, depth, x + pwByDepth, y + phByDepth, x - pwByDepth, y - phByDepth, ao);
+    pwByDepth *= 1.2;
+    phByDepth *= 1.2;
+    addAO(i, depth, x + pwByDepth, y, x, y - phByDepth, ao);
+    // sample jittering:
+    //	pw += random.x * 0.0007;
+    //	ph += random.y * 0.0007;
+    // increase sampling area:
+    pw *= 1.7;
+    ph *= 1.7;
+
+  }
+
+  ao = ao / float(iterations * 8);
+  ao *= 0.4 * foggy;
+  ao = clamp(ao, 0.0, 1.0);
+
+  //gl_FragColor = vec4(0.5 - vao, max(0.5, ao));
+  gl_FragColor =  vec4(color.rgb - ao, max(color.a, ao));
+}
+
diff --git a/vtm/resources/assets/shaders/post_fxaa.glsl b/vtm/resources/assets/shaders/post_fxaa.glsl
new file mode 100644
index 00000000..823bb5ea
--- /dev/null
+++ b/vtm/resources/assets/shaders/post_fxaa.glsl
@@ -0,0 +1,117 @@
+#ifdef GLES
+precision highp float;
+#endif
+uniform vec2 u_pixel;
+attribute vec4 a_pos;
+varying vec2 tex_pos;
+varying vec2 tex_nw;
+varying vec2 tex_ne;
+varying vec2 tex_sw;
+varying vec2 tex_se;
+void
+main()
+{
+  gl_Position = a_pos;
+  tex_pos = (a_pos.xy + 1.0) * 0.5;
+  vec2 pixel = u_pixel * 2.5;
+  tex_nw = tex_pos + vec2(-1.0, -1.0) * pixel;
+  tex_ne = tex_pos + vec2(1.0, -1.0) * pixel;
+  tex_sw = tex_pos + vec2(-1.0, 1.0) * pixel;
+  tex_se = tex_pos + vec2(1.0, 1.0) * pixel;
+}
+
+$$
+
+#ifdef GLES
+precision highp float;
+#endif
+uniform sampler2D u_texColor;
+uniform vec2 u_pixel;
+varying vec2 tex_pos;
+varying vec2 tex_nw;
+varying vec2 tex_ne;
+varying vec2 tex_sw;
+varying vec2 tex_se;
+
+const vec2 m_SpanMax = vec2(2.5, 2.5);
+const float m_ReduceMul = 0.15;
+const vec3 luma = vec3(0.299, 0.587, 0.114);
+
+#define FXAA_REDUCE_MIN   (1.0/128.0)
+
+//float FxaaLuma(float3 rgb) {
+//  return rgb.g * (0.587/0.299) + rgb.b;
+//}
+
+void
+main(){
+  vec2 pixel = u_pixel * 3.15;
+
+  vec4 rgbNW = texture2D(u_texColor, tex_nw);
+  vec4 rgbNE = texture2D(u_texColor, tex_ne);
+  vec4 rgbSW = texture2D(u_texColor, tex_sw);
+  vec4 rgbSE = texture2D(u_texColor, tex_se);
+
+  vec4 rgbM = texture2D(u_texColor, tex_pos);
+
+  if (rgbNW.a + rgbNE.a + rgbSW.a + rgbSE.a < 0.1) {
+    gl_FragColor = rgbM;
+    return;
+  }
+  //return vec4(rgbM - (rgbNW + rgbNE)*0.25,1.0);
+
+  float lumaNW = dot(rgbNW.rgb, luma);
+  float lumaNE = dot(rgbNE.rgb, luma);
+  float lumaSW = dot(rgbSW.rgb, luma);
+  float lumaSE = dot(rgbSE.rgb, luma);
+  float lumaM = dot(rgbM.rgb, luma);
+
+  float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));
+  float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));
+
+  //vec4 rgb = texture2D (tex, tex_pos);
+  //return vec4(0.5 + lumaM - lumaMin, lumaM, 0.5 + lumaM - lumaMax, 1.0) * rgb.a;
+
+  vec2 dir;
+  dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));
+  dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));
+
+//  if (max(dir.x, dir.y) > 0.1){
+//    gl_FragColor = vec4(dir*0.5, 0.0, 0.5);
+//  } else{
+//    gl_FragColor = vec4(rgbM.rgb * 0.2, 0.8);
+//  }
+//  return;
+
+  float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * m_ReduceMul), FXAA_REDUCE_MIN);
+
+  float rcpDirMin = 1.0 / (dirReduce + min(abs(dir.x), abs(dir.y)));
+
+  dir = min(m_SpanMax, max(-m_SpanMax, dir * rcpDirMin)) * pixel;
+
+  vec4 rgbA = 0.5 * (texture2D(u_texColor, tex_pos + dir * vec2(1.0 / 3.0 - 0.5))
+      + texture2D(u_texColor, tex_pos + dir * vec2(2.0 / 3.0 - 0.5)));
+
+  vec4 rgbB = rgbA * 0.5 + 0.25 * (texture2D(u_texColor, tex_pos + dir * vec2(0.0 / 3.0 - 0.5))
+      + texture2D(u_texColor, tex_pos + dir * vec2(3.0 / 3.0 - 0.5)));
+
+  float lumaB = dot(rgbB.rgb, luma.rgb);
+
+  float d = step(lumaB, lumaMin) + step(lumaMax, lumaB);
+
+  gl_FragColor = (1.0 - d) * rgbB + d * rgbA;
+
+  //gl_FragColor = vec4(rgbM.rgb * 0.5, 1.0) + vec4(((1.0 - d) * rgbB.rgb + d * rgbA.rgb) - rgbM.rgb, 1.0) * 2.0;
+
+//  if ((lumaB < lumaMin) || (lumaB > lumaMax))
+//    { return rgbA; } else { return rgbB; }
+}
+
+//vec2 rcpFrame = vec2(1.0) / g_Resolution;
+//gl_FragColor = vec4(FxaaPixelShader(tex_pos, m_Texture, rcpFrame), 1.0);
+//gl_FragColor = FxaaPixelShader(tex_pos, u_texColor, u_pixel * 3.15);
+//gl_FragColor = FxaaPixelShader (tex_pos, u_texColor, u_pixel * 2.35);
+//vec4 c = texture2D (u_texColor, tex_pos);
+//gl_FragColor = vec4 (c.rgb * 0.5, 1.0) + vec4(FxaaPixelShader (tex_pos, u_texColor, u_pixel * 3.15).rgb - c.rgb, 1.0) * 2.0;
+//gl_FragColor = 0.2*c + (FxaaPixelShader (tex_pos, u_texColor, u_pixel * 1.2)) * 0.8;
+//}
diff --git a/vtm/resources/assets/shaders/post_ssao.glsl b/vtm/resources/assets/shaders/post_ssao.glsl
new file mode 100644
index 00000000..37d3bea0
--- /dev/null
+++ b/vtm/resources/assets/shaders/post_ssao.glsl
@@ -0,0 +1,143 @@
+#ifdef GLES
+precision highp float;
+#endif
+uniform vec2 u_pixel;
+attribute vec4 a_pos;
+varying vec2 tex_pos;
+void
+main(){
+  gl_Position = a_pos;
+  tex_pos = (a_pos.xy + 1.0) * 0.5;
+}
+
+$$
+
+#ifdef GLES
+precision highp float;
+#endif
+uniform sampler2D u_tex;
+uniform sampler2D u_texColor;
+uniform vec2 u_pixel;
+
+varying vec2 tex_pos;
+// gauss bell center
+const float gdisplace = 0.2;
+const float nearZ = 1.0;
+const float farZ = 4.0;
+const int iterations = 4;
+
+float getDepth(float posZ) {
+  return (2.0 * nearZ) / (nearZ + farZ - posZ * (farZ - nearZ));
+}
+float compareDepths(in float depth1, in float depth2, inout float far) {
+  //   depth difference (0-100)
+  float diff = (depth1 - depth2) * 100.0;
+  //   set 'far == 1.0' when 'diff' > 'gdisplace'
+  far = step(diff, gdisplace);
+  // gauss bell width 2,
+  // if far reduce left bell width to avoid self-shadowing
+  float garea = max((1.0 - far) * 2.0, 0.1);
+
+  //return (step(diff, 0.0) * -0.1) + pow(2.7182, -2.0 * pow(diff - gdisplace,2.0) / pow(garea, 2.0));
+  return  pow(2.7182, -2.0 * pow(diff - gdisplace,2.0) / pow(garea, 2.0));
+}
+
+void
+addAO(in float depth, in float x1, in float y1, in float x2, in float y2, inout float ao){
+  float z_11 = getDepth(texture2D(u_tex, vec2(x1, y1)).x);
+  float z_12 = getDepth(texture2D(u_tex, vec2(x1, y2)).x);
+  float z_21 = getDepth(texture2D(u_tex, vec2(x2, y1)).x);
+  float z_22 = getDepth(texture2D(u_tex, vec2(x2, y2)).x);
+  //depth = 0.99 * depth + (z_11 + z_12 + z_21 + z_22) * (0.25 * 0.01);
+
+
+  float f_11;
+  float d_11 = compareDepths(depth, z_11, f_11);
+
+  float f_12;
+  float d_12 = compareDepths(depth, z_12, f_12);
+
+  float f_21;
+  float d_21 = compareDepths(depth, z_21, f_21);
+
+  float f_22;
+  float d_22 = compareDepths(depth, z_22, f_22);
+
+  ao += 1.0 //(1.0 - step(1.0, x1)) * (1.0 - step(1.0, y1))
+      * (d_11 + f_11 * (1.0 - d_11) * d_22);
+
+  ao += 1.0 //(1.0 - step(1.0, x1)) * step(0.0, y2)
+      * (d_12 + f_12 * (1.0 - d_12) * d_21);
+
+  ao += 1.0 //step(0.0, x2) * (1.0 - step(1.0, y1))
+      * (d_21 + f_21 * (1.0 - d_21) * d_12);
+
+  ao += 1.0 //step(0.0, x2) * step(0.0, y2)
+      * (d_22 + f_22 * (1.0 - d_22) * d_11);
+}
+
+void
+main(void){
+  //   randomization texture:
+  //	 vec2 fres = vec2(20.0, 20.0);
+  //	vec3 random = texture2D(rand, gl_TexCoord[0].st * fres.xy);
+  //	random = random * 2.0 - vec3(1.0);
+  vec4 color = texture2D(u_texColor, tex_pos);
+  float depth = texture2D(u_tex, tex_pos).x;
+
+  float fog = pow(depth,3.0);
+  //return;
+  depth = getDepth(depth);
+
+  float x = tex_pos.x;
+  float y = tex_pos.y;
+  float pw = u_pixel.x;
+  float ph = u_pixel.y;
+  float ao = 0.0;
+
+  for (int i = 0; i < iterations; i++) {
+    float pwByDepth = pw / depth;
+    float phByDepth = ph / depth;
+    addAO(depth, x + pwByDepth, y + phByDepth, x - pwByDepth, y - phByDepth, ao);
+    pwByDepth *= 1.2;
+    phByDepth *= 1.2;
+    addAO(depth, x + pwByDepth, y, x, y - phByDepth, ao);
+    // sample jittering:
+    //	pw += random.x * 0.0007;
+    //	ph += random.y * 0.0007;
+    // increase sampling area:
+    pw *= 1.7;
+    ph *= 1.7;
+
+  }
+
+  //vec3 vao = vec3(fog * pow(ao / float(iterations * 8), 1.2));
+  ao = ao / float(iterations * 8);
+  ao *= 0.2;
+  ao = clamp(ao, 0.0, 1.0);
+
+  vec3 vao = vec3(ao);
+
+  //gl_FragColor = vec4(0.5 - vao, max(0.5, ao));
+  gl_FragColor =  vec4(color.rgb - ao, max(color.a, ao));
+
+
+  //gl_FragColor =  color - (fog * vec4(vao, 0.0));
+
+
+  //gl_FragColor =  vec4(vec3(0.8) - vao, 1.0);
+
+  //color *= 0.5;
+
+  //gl_FragColor =  vec4(color.rgb - fog * vao, max(color.a, ao));
+
+
+  //gl_FragColor =  vec4(1.0) - (vec4(vao, 0.0));
+  //gl_FragColor =  vec4((color.rgb + vao)*0.5, color.a);
+
+  // }
+  //	 gl_FragColor = vec4(vao, 1.0) * texture2D(u_texColor, tex_pos.xy);
+  //	gl_FragColor = vec4(gl_TexCoord[0].xy,0.0,1.0);
+  //	gl_FragColor = vec4(tex_pos.xy,0.0,1.0);
+  //	 gl_FragColor = vec4(gl_FragCoord.xy / u_screen, 0.0, 1.0);
+}
diff --git a/vtm/src/org/oscim/layers/tile/s3db/S3DBLayer.java b/vtm/src/org/oscim/layers/tile/s3db/S3DBLayer.java
index 6df7ecd5..df21c952 100644
--- a/vtm/src/org/oscim/layers/tile/s3db/S3DBLayer.java
+++ b/vtm/src/org/oscim/layers/tile/s3db/S3DBLayer.java
@@ -7,6 +7,7 @@ import org.oscim.layers.tile.TileRenderer;
 import org.oscim.map.Map;
 import org.oscim.renderer.ExtrusionRenderer;
 import org.oscim.renderer.GLViewport;
+import org.oscim.renderer.OffscreenRenderer;
 import org.oscim.tiling.TileSource;
 import org.oscim.utils.ColorUtil;
 import org.oscim.utils.ColorsCSS;
@@ -39,23 +40,29 @@ public class S3DBLayer extends TileLayer {
 		return new S3DBTileLoader(getManager(), mTileSource);
 	}
 
-	static class S3DBRenderer extends TileRenderer {
+	public static class S3DBRenderer extends TileRenderer {
 		ExtrusionRenderer mExtRenderer;
+		OffscreenRenderer or;
 
 		public S3DBRenderer() {
 			mExtRenderer = new ExtrusionRenderer(this, 16, true, false);
+
+			or = new OffscreenRenderer();
+			or.setRenderer(mExtRenderer);
 		}
 
 		@Override
 		protected synchronized void update(GLViewport v) {
 			super.update(v);
-			mExtRenderer.update(v);
-			setReady(mExtRenderer.isReady());
+			//mExtRenderer.update(v);
+			or.update(v);
+			setReady(or.isReady());
 		}
 
 		@Override
 		protected synchronized void render(GLViewport v) {
-			mExtRenderer.render(v);
+			or.render(v);
+			//mExtRenderer.render(v);
 		}
 	}
 
@@ -97,7 +104,7 @@ public class S3DBLayer extends TileLayer {
 			return Color.LTGRAY;
 
 		if ("transparent" == color)
-			return Color.get(64, 64, 64, 64);
+			return Color.get(0, 1, 1, 1);
 
 		Integer css = ColorsCSS.get(color);
 
diff --git a/vtm/src/org/oscim/layers/tile/s3db/S3DBTileLoader.java b/vtm/src/org/oscim/layers/tile/s3db/S3DBTileLoader.java
index 66ad95f7..92c1abc8 100644
--- a/vtm/src/org/oscim/layers/tile/s3db/S3DBTileLoader.java
+++ b/vtm/src/org/oscim/layers/tile/s3db/S3DBTileLoader.java
@@ -3,9 +3,11 @@ package org.oscim.layers.tile.s3db;
 import static org.oscim.layers.tile.s3db.S3DBLayer.getMaterialColor;
 
 import org.oscim.backend.canvas.Color;
+import org.oscim.core.GeometryBuffer;
 import org.oscim.core.GeometryBuffer.GeometryType;
 import org.oscim.core.MapElement;
 import org.oscim.core.MercatorProjection;
+import org.oscim.core.Tag;
 import org.oscim.layers.tile.MapTile;
 import org.oscim.layers.tile.TileLoader;
 import org.oscim.layers.tile.TileManager;
@@ -27,9 +29,25 @@ class S3DBTileLoader extends TileLoader {
 
 	private float mGroundScale;
 
+	static MapElement mTilePlane = new MapElement();
+
+	static {
+		mTilePlane = new MapElement();
+		GeometryBuffer g = mTilePlane;
+		g.type = GeometryType.TRIS;
+		g.points = new float[] {
+		        0, 0, 0,
+		        4096, 0, 0,
+		        0, 4096, 0,
+		        4096, 4096, 0 };
+		g.index = new short[] { 0, 1, 2, 2, 1, 3 };
+		mTilePlane.tags.add(new Tag("c", "transparent"));
+	}
+
 	public S3DBTileLoader(TileManager tileManager, TileSource tileSource) {
 		super(tileManager);
 		mTileDataSource = tileSource.getDataSource();
+
 	}
 
 	@Override
@@ -45,15 +63,18 @@ class S3DBTileLoader extends TileLoader {
 		mGroundScale = (float) MercatorProjection
 		    .groundResolution(lat, 1 << mTile.zoomLevel);
 
+		mRoofs = new ExtrusionLayer(0, mGroundScale, Color.get(247, 249, 250));
+
 		mLayers = new ExtrusionLayer(0, mGroundScale, Color.get(255, 254, 252));
 		//mRoofs = new ExtrusionLayer(0, mGroundScale, Color.get(207, 209, 210));
-		mRoofs = new ExtrusionLayer(0, mGroundScale, Color.get(247, 249, 250));
-		mLayers.next = mRoofs;
+		mRoofs.next = mLayers;
 
 		ElementLayers layers = new ElementLayers();
-		layers.setExtrusionLayers(mLayers);
+		layers.setExtrusionLayers(mRoofs);
 		tile.data = layers;
 
+		process(mTilePlane);
+
 		try {
 			/* query database, which calls process() callback */
 			mTileDataSource.query(mTile, this);
@@ -109,16 +130,18 @@ class S3DBTileLoader extends TileLoader {
 		}
 		ExtrusionLayer l = new ExtrusionLayer(0, mGroundScale, c);
 
-		l.next = mRoofs.next;
-		mRoofs.next = l;
+		l.next = mLayers.next;
+		mLayers.next = l;
 
 		l.add(element);
 	}
 
 	@Override
 	public void completed(QueryResult result) {
+
 		mLayers = null;
 		mRoofs = null;
+
 		super.completed(result);
 	}
 }
diff --git a/vtm/src/org/oscim/layers/tile/vector/BuildingLayer.java b/vtm/src/org/oscim/layers/tile/vector/BuildingLayer.java
index 4abdfdb1..834b5f27 100644
--- a/vtm/src/org/oscim/layers/tile/vector/BuildingLayer.java
+++ b/vtm/src/org/oscim/layers/tile/vector/BuildingLayer.java
@@ -25,6 +25,7 @@ import org.oscim.layers.tile.vector.VectorTileLayer.TileLoaderThemeHook;
 import org.oscim.map.Map;
 import org.oscim.renderer.ExtrusionRenderer;
 import org.oscim.renderer.GLViewport;
+import org.oscim.renderer.OffscreenRenderer;
 import org.oscim.renderer.elements.ElementLayers;
 import org.oscim.renderer.elements.ExtrusionLayer;
 import org.oscim.theme.styles.ExtrusionStyle;
@@ -35,14 +36,22 @@ public class BuildingLayer extends Layer implements TileLoaderThemeHook {
 	//static final Logger log = LoggerFactory.getLogger(BuildingOverlay.class);
 
 	private final static int MIN_ZOOM = 17;
+	private final static boolean POST_AA = false;
+
 	private final int mMinZoom;
+	private ExtrusionRenderer mExtRenderer;
 
 	public BuildingLayer(Map map, VectorTileLayer tileLayer) {
-		super(map);
-		tileLayer.addHook(this);
+		this(map, tileLayer, MIN_ZOOM);
 
-		mMinZoom = MIN_ZOOM;
-		mRenderer = new ExtrusionRenderer(tileLayer.tileRenderer(), MIN_ZOOM);
+		//		super(map);
+		//		tileLayer.addHook(this);
+		//
+		//		mMinZoom = MIN_ZOOM;
+		//
+		//		OffscreenRenderer or = new OffscreenRenderer();
+		//		or.setRenderer(new ExtrusionRenderer(tileLayer.tileRenderer(), MIN_ZOOM));
+		//		mRenderer = or;
 	}
 
 	public BuildingLayer(Map map, VectorTileLayer tileLayer, int minZoom) {
@@ -50,8 +59,7 @@ public class BuildingLayer extends Layer implements TileLoaderThemeHook {
 		tileLayer.addHook(this);
 
 		mMinZoom = minZoom;
-		mRenderer = new ExtrusionRenderer(tileLayer.tileRenderer(), mMinZoom) {
-
+		mExtRenderer = new ExtrusionRenderer(tileLayer.tileRenderer(), mMinZoom) {
 			private long mStartTime;
 
 			@Override
@@ -90,6 +98,14 @@ public class BuildingLayer extends Layer implements TileLoaderThemeHook {
 				super.update(v);
 			}
 		};
+
+		if (POST_AA) {
+			OffscreenRenderer or = new OffscreenRenderer();
+			or.setRenderer(mExtRenderer);
+			mRenderer = or;
+		} else {
+			mRenderer = mExtRenderer;
+		}
 	}
 
 	private final float mFadeTime = 500;
diff --git a/vtm/src/org/oscim/renderer/MapRenderer.java b/vtm/src/org/oscim/renderer/MapRenderer.java
index 0b38a18d..f72e6734 100644
--- a/vtm/src/org/oscim/renderer/MapRenderer.java
+++ b/vtm/src/org/oscim/renderer/MapRenderer.java
@@ -178,11 +178,11 @@ public class MapRenderer {
 
 	private void draw() {
 
-		if (mUpdateColor) {
-			float cc[] = mClearColor;
-			GL.glClearColor(cc[0], cc[1], cc[2], cc[3]);
-			mUpdateColor = false;
-		}
+		//if (mUpdateColor) {
+		float cc[] = mClearColor;
+		GL.glClearColor(cc[0], cc[1], cc[2], cc[3]);
+		mUpdateColor = false;
+		//}
 
 		GL.glDepthMask(true);
 		GL.glStencilMask(0xFF);
@@ -320,6 +320,7 @@ public class MapRenderer {
 		GLState.init(GL);
 		GLUtils.init(GL);
 		GLShader.init(GL);
+		OffscreenRenderer.init(GL);
 
 		// Set up some vertex buffer objects
 		BufferObject.init(GL, 200);
diff --git a/vtm/src/org/oscim/renderer/OffscreenRenderer.java b/vtm/src/org/oscim/renderer/OffscreenRenderer.java
new file mode 100644
index 00000000..8d3fa863
--- /dev/null
+++ b/vtm/src/org/oscim/renderer/OffscreenRenderer.java
@@ -0,0 +1,197 @@
+package org.oscim.renderer;
+
+import java.nio.IntBuffer;
+
+import org.oscim.backend.GL20;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class OffscreenRenderer extends LayerRenderer {
+	final static Logger log = LoggerFactory.getLogger(OffscreenRenderer.class);
+
+	int fb;
+	int renderTex;
+	int renderDepth;
+
+	int texW = -1;
+	int texH = -1;
+
+	boolean initialized;
+
+	private boolean useDepthTexture = false;
+
+	static class Shader extends GLShader {
+		int aPos, uTexDepth, uTexColor, uPixel;
+
+		Shader(String shaderFile) {
+			if (!create(shaderFile))
+				return;
+			aPos = getAttrib("a_pos");
+			uTexColor = getUniform("u_texColor");
+			uTexDepth = getUniform("u_tex");
+			uPixel = getUniform("u_pixel");
+		}
+	}
+
+	protected boolean setupFBO(GLViewport viewport) {
+		IntBuffer buf = MapRenderer.getIntBuffer(1);
+
+		texW = (int) viewport.getWidth();
+		texH = (int) viewport.getHeight();
+
+		GL.glGenFramebuffers(1, buf);
+		fb = buf.get(0);
+
+		buf.clear();
+		GL.glGenTextures(1, buf);
+		renderTex = buf.get(0);
+
+		GLUtils.checkGlError("0");
+
+		GL.glBindFramebuffer(GL20.GL_FRAMEBUFFER, fb);
+
+		// generate color texture
+		GL.glBindTexture(GL20.GL_TEXTURE_2D, renderTex);
+
+		GLUtils.setTextureParameter(GL20.GL_NEAREST,
+		                            GL20.GL_NEAREST,
+		                            GL20.GL_CLAMP_TO_EDGE,
+		                            GL20.GL_CLAMP_TO_EDGE);
+
+		GL.glTexImage2D(GL20.GL_TEXTURE_2D, 0,
+		                GL20.GL_RGBA, texW, texH, 0, GL20.GL_RGBA,
+		                GL20.GL_UNSIGNED_BYTE, null);
+
+		GL.glFramebufferTexture2D(GL20.GL_FRAMEBUFFER,
+		                          GL20.GL_COLOR_ATTACHMENT0,
+		                          GL20.GL_TEXTURE_2D,
+		                          renderTex, 0);
+		GLUtils.checkGlError("1");
+
+		if (useDepthTexture) {
+			buf.clear();
+			GL.glGenTextures(1, buf);
+			renderDepth = buf.get(0);
+			GL.glBindTexture(GL20.GL_TEXTURE_2D, renderDepth);
+			GLUtils.setTextureParameter(GL20.GL_NEAREST,
+			                            GL20.GL_NEAREST,
+			                            GL20.GL_CLAMP_TO_EDGE,
+			                            GL20.GL_CLAMP_TO_EDGE);
+
+			GL.glTexImage2D(GL20.GL_TEXTURE_2D, 0,
+			                GL20.GL_DEPTH_COMPONENT,
+			                texW, texH, 0,
+			                GL20.GL_DEPTH_COMPONENT,
+			                GL20.GL_UNSIGNED_SHORT, null);
+
+			GL.glFramebufferTexture2D(GL20.GL_FRAMEBUFFER,
+			                          GL20.GL_DEPTH_ATTACHMENT,
+			                          GL20.GL_TEXTURE_2D,
+			                          renderDepth, 0);
+		} else {
+			buf.clear();
+			GL.glGenRenderbuffers(1, buf);
+			int depthRenderbuffer = buf.get(0);
+
+			GL.glBindRenderbuffer(GL20.GL_RENDERBUFFER, depthRenderbuffer);
+
+			GL.glRenderbufferStorage(GL20.GL_RENDERBUFFER,
+			                         GL20.GL_DEPTH_COMPONENT16,
+			                         texW, texH);
+
+			GL.glFramebufferRenderbuffer(GL20.GL_FRAMEBUFFER,
+			                             GL20.GL_DEPTH_ATTACHMENT,
+			                             GL20.GL_RENDERBUFFER,
+			                             depthRenderbuffer);
+		}
+
+		GLUtils.checkGlError("2");
+
+		int status = GL.glCheckFramebufferStatus(GL20.GL_FRAMEBUFFER);
+		GL.glBindFramebuffer(GL20.GL_FRAMEBUFFER, 0);
+		GL.glBindTexture(GL20.GL_TEXTURE_2D, 0);
+
+		if (status != GL20.GL_FRAMEBUFFER_COMPLETE) {
+			log.debug("invalid framebuffer! " + status);
+			return false;
+		}
+		return true;
+	}
+
+	static void init(GL20 gl20) {
+		GL = gl20;
+		shaders[0] = new Shader("post_fxaa");
+		shaders[1] = new Shader("post_ssao");
+		shaders[2] = new Shader("post_combined");
+	}
+
+	static Shader[] shaders = new Shader[3];
+
+	public void enable(boolean on) {
+		if (on)
+			GL.glBindFramebuffer(GL20.GL_FRAMEBUFFER, fb);
+		else
+			GL.glBindFramebuffer(GL20.GL_FRAMEBUFFER, 0);
+	}
+
+	public void begin() {
+		GL.glBindFramebuffer(GL20.GL_FRAMEBUFFER, fb);
+		GL.glDepthMask(true);
+		GL.glClear(GL20.GL_DEPTH_BUFFER_BIT);
+	}
+
+	LayerRenderer mRenderer;
+
+	public void setRenderer(LayerRenderer renderer) {
+		mRenderer = renderer;
+	}
+
+	@Override
+	public void update(GLViewport viewport) {
+		if (texW != viewport.getWidth() || texH != viewport.getHeight())
+			setupFBO(viewport);
+
+		mRenderer.update(viewport);
+		setReady(mRenderer.isReady());
+	}
+
+	@Override
+	public void render(GLViewport viewport) {
+		GL.glBindFramebuffer(GL20.GL_FRAMEBUFFER, fb);
+		GL.glViewport(0, 0, texW, texH);
+		GL.glDepthMask(true);
+		GL.glClearColor(0, 0, 0, 0);
+		GL.glClear(GL20.GL_DEPTH_BUFFER_BIT | GL20.GL_COLOR_BUFFER_BIT);
+
+		mRenderer.render(viewport);
+
+		GL.glBindFramebuffer(GL20.GL_FRAMEBUFFER, 0);
+
+		Shader s = shaders[0];
+		s.useProgram();
+
+		/* bind depth texture */
+		if (useDepthTexture) {
+			GL.glActiveTexture(GL20.GL_TEXTURE1);
+			GLState.bindTex2D(renderDepth);
+			GL.glUniform1i(s.uTexDepth, 1);
+			GL.glActiveTexture(GL20.GL_TEXTURE0);
+		}
+		/* bind color texture */
+		GLState.bindTex2D(renderTex);
+		GL.glUniform1i(s.uTexColor, 0);
+
+		MapRenderer.bindQuadVertexVBO(s.aPos, true);
+
+		GL.glUniform2f(s.uPixel,
+		               (float) (1.0 / texW * 0.5),
+		               (float) (1.0 / texH * 0.5));
+
+		GLState.enableVertexArrays(s.aPos, -1);
+
+		GLState.test(false, false);
+		GLState.blend(true);
+		GL.glDrawArrays(GL20.GL_TRIANGLE_STRIP, 0, 4);
+		GLUtils.checkGlError("....");
+	}
+}