diff --git a/demos/heatmap.html b/demos/heatmap.html
index ed25b8dbed..0a5d3ff327 100644
--- a/demos/heatmap.html
+++ b/demos/heatmap.html
@@ -13,14 +13,14 @@
 </head>
 <body>
 <div id="map"></div>
-<script src="https://webapi.amap.com/maps?v=1.4.8&key=15cd8a57710d40c9b7c0e3cc120f1200&plugin=Map3D"></script>
+<script src="https://webapi.amap.com/maps?v=1.4.8&key=f28fca5384129d180ad82915156a9baf&plugin=Map3D"></script>
 <script src="./assets/jquery-3.2.1.min.js"></script>
 <script src="./assets/dat.gui.min.js"></script>
 <script src="../build/L7.js"></script>
 <script>
 const scene = new L7.Scene({
   id: 'map',
-  mapStyle: 'dark', // 样式URL
+  mapStyle: 'amap://styles/c9f1d10cae34f8ab05e425462c5a58d7', // 样式URL
   center: [ -155, 60 ],
   pitch: 0,
   zoom: 4.5
@@ -35,11 +35,12 @@ scene.on('loaded', () => {
     .source(data)
     .size('mag', [ 0, 1 ]) // weight映射通道
     .style({
-      intensity: 100,
-      radius: 30,
+      intensity: 10,
+      radius: 10,
+      opacity:1,
       rampColors: {
-        colors: [ 'rgba(33,102,172,0.0)', 'rgb(103,169,207)', 'rgb(209,229,240)', 'rgb(253,219,199)', 'rgb(239,138,98)', 'rgb(178,24,43,1.0)' ],
-        positions: [ 0, 0.2, 0.4, 0.6, 0.8, 1.0 ]
+        colors: [ '#ffda45ff', '#fde725ff', '#ffc934ff', '#ffb824ff', '#ffb824ff', '#fa8100ff' ],
+        positions: [ 0, 0.2, 0.4, 0.6, 0.9, 1.0 ]
       }
     })
     .render();
diff --git a/package.json b/package.json
index 5a8d96aa9a..7dc407177f 100755
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@antv/l7",
-  "version": "1.1.5",
+  "version": "1.1.7",
   "description": "Large-scale WebGL-powered Geospatial Data Visualization",
   "main": "build/l7.js",
   "browser": "build/l7.js",
@@ -111,6 +111,7 @@
     "polyline-normals": "^2.0.2",
     "rbush": "^2.0.2",
     "simple-statistics": "^7.0.1",
+    "supercluster": "^6.0.1",
     "three": "^0.101.1",
     "venn.js": "^0.2.20",
     "viewport-mercator-project": "^5.2.0",
diff --git a/src/core/engine/composer.js b/src/core/engine/composer.js
old mode 100644
new mode 100755
index 5a1e2672ce..0575cfe3f2
--- a/src/core/engine/composer.js
+++ b/src/core/engine/composer.js
@@ -1,10 +1,10 @@
 // jscs:disable
 /* eslint-disable */
 
-import THREE from '../three';
-import CopyShader from './CopyShader';
-import ShaderPass from './ShaderPass';
-import MaskPass, {ClearMaskPass} from './MaskPass';
+import * as THREE from '../three';
+import CopyShader from './copy-shader';
+import ShaderPass from './shader-pass';
+import MaskPass, {ClearMaskPass} from './mask-pass';
 
 /**
  * @author alteredq / http://alteredqualia.com/
@@ -50,7 +50,8 @@ EffectComposer.prototype = {
 		this.writeBuffer = tmp;
 
 	},
-
+	visible:true,
+	type:'composer',
 	addPass: function ( pass ) {
 
 		this.passes.push( pass );
@@ -71,7 +72,6 @@ EffectComposer.prototype = {
 		var maskActive = false;
 
 		var pass, i, il = this.passes.length;
-
 		for ( i = 0; i < il; i ++ ) {
 
 			pass = this.passes[ i ];
@@ -147,4 +147,3 @@ EffectComposer.prototype = {
 };
 
 export default EffectComposer;
-THREE.EffectComposer = EffectComposer;
diff --git a/src/core/engine/copy-shader.js b/src/core/engine/copy-shader.js
new file mode 100755
index 0000000000..afed95ae63
--- /dev/null
+++ b/src/core/engine/copy-shader.js
@@ -0,0 +1,53 @@
+// jscs:disable
+/* eslint-disable */
+
+import * as THREE from '../three';
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ *
+ * Full-screen textured quad shader
+ */
+
+var CopyShader = {
+
+	uniforms: {
+
+		"tDiffuse": { type: "t", value: null },
+		"opacity":  { type: "f", value: 1.0 }
+
+	},
+
+	vertexShader: [
+
+		"varying vec2 vUv;",
+
+		"void main() {",
+
+			"vUv = uv;",
+			"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+
+		"}"
+
+	].join( "\n" ),
+
+	fragmentShader: [
+
+		"uniform float opacity;",
+
+		"uniform sampler2D tDiffuse;",
+
+		"varying vec2 vUv;",
+
+		"void main() {",
+
+			"vec4 texel = texture2D( tDiffuse, vUv );",
+			"gl_FragColor = opacity * texel;",
+
+		"}"
+
+	].join( "\n" )
+
+};
+
+export default CopyShader;
diff --git a/src/core/engine/effect-composer.js b/src/core/engine/effect-composer.js
new file mode 100755
index 0000000000..23a8d690ed
--- /dev/null
+++ b/src/core/engine/effect-composer.js
@@ -0,0 +1,21 @@
+import EffectComposer from './composer';
+
+export default function(renderer, container) {
+  const composer = new EffectComposer(renderer);
+
+  const updateSize = function() {
+    // TODO: Re-enable this when perf issues can be solved
+    //
+    // Rendering double the resolution of the screen can be really slow
+    // var pixelRatio = window.devicePixelRatio;
+    const pixelRatio = 1;
+
+    composer.setSize(container.clientWidth * pixelRatio, container.clientHeight * pixelRatio);
+  };
+
+  window.addEventListener('resize', updateSize, false);
+  updateSize();
+
+  return composer;
+}
+
diff --git a/src/core/engine/index.js b/src/core/engine/index.js
index a12f2e06f6..a08bc77d76 100644
--- a/src/core/engine/index.js
+++ b/src/core/engine/index.js
@@ -16,14 +16,16 @@ export default class Engine extends EventEmitter {
     this._scene.add(this.world);
     this._picking = Picking(this._world, this._renderer, this._camera, this._scene);
     this.clock = new THREE.Clock();
+    this.composerLayers = [];
   }
   _initPostProcessing() {
-
+    this.composerLayers.forEach(layer => {
+      layer.visible && layer.render();
+    });
   }
   update() {
-
     this._renderer.render(this._scene, this._camera);
-
+    this._initPostProcessing();
   }
   destroy() {
 
diff --git a/src/core/engine/mask-pass.js b/src/core/engine/mask-pass.js
new file mode 100755
index 0000000000..b4f965bbb7
--- /dev/null
+++ b/src/core/engine/mask-pass.js
@@ -0,0 +1,94 @@
+// jscs:disable
+/* eslint-disable */
+
+import * as THREE from '../three';
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+var MaskPass = function ( scene, camera ) {
+
+	this.scene = scene;
+	this.camera = camera;
+
+	this.enabled = true;
+	this.clear = true;
+	this.needsSwap = false;
+
+	this.inverse = false;
+
+};
+
+MaskPass.prototype = {
+
+	render: function ( renderer, writeBuffer, readBuffer, delta ) {
+
+		var context = renderer.context;
+
+		// don't update color or depth
+
+		context.colorMask( false, false, false, false );
+		context.depthMask( false );
+
+		// set up stencil
+
+		var writeValue, clearValue;
+
+		if ( this.inverse ) {
+
+			writeValue = 0;
+			clearValue = 1;
+
+		} else {
+
+			writeValue = 1;
+			clearValue = 0;
+
+		}
+
+		context.enable( context.STENCIL_TEST );
+		context.stencilOp( context.REPLACE, context.REPLACE, context.REPLACE );
+		context.stencilFunc( context.ALWAYS, writeValue, 0xffffffff );
+		context.clearStencil( clearValue );
+
+		// draw into the stencil buffer
+
+		renderer.render( this.scene, this.camera, readBuffer, this.clear );
+		renderer.render( this.scene, this.camera, writeBuffer, this.clear );
+
+		// re-enable update of color and depth
+
+		context.colorMask( true, true, true, true );
+		context.depthMask( true );
+
+		// only render where stencil is set to 1
+
+		context.stencilFunc( context.EQUAL, 1, 0xffffffff );  // draw if == 1
+		context.stencilOp( context.KEEP, context.KEEP, context.KEEP );
+
+	}
+
+};
+
+
+var ClearMaskPass = function () {
+
+	this.enabled = true;
+
+};
+
+ClearMaskPass.prototype = {
+
+	render: function ( renderer, writeBuffer, readBuffer, delta ) {
+
+		var context = renderer.context;
+
+		context.disable( context.STENCIL_TEST );
+
+	}
+
+};
+
+export default MaskPass;
+export {ClearMaskPass as ClearMaskPass};
diff --git a/src/core/engine/render-pass.js b/src/core/engine/render-pass.js
new file mode 100644
index 0000000000..5a0dfc384c
--- /dev/null
+++ b/src/core/engine/render-pass.js
@@ -0,0 +1,58 @@
+// jscs:disable
+/* eslint-disable */
+
+import * as THREE from '../three';
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+var RenderPass = function ( scene, camera, overrideMaterial, clearColor, clearAlpha ) {
+
+	this.scene = scene;
+	this.camera = camera;
+
+	this.overrideMaterial = overrideMaterial;
+
+	this.clearColor = clearColor;
+	this.clearAlpha = ( clearAlpha !== undefined ) ? clearAlpha : 1;
+
+	this.oldClearColor = new THREE.Color();
+	this.oldClearAlpha = 1;
+
+	this.enabled = true;
+	this.clear = false;
+	this.needsSwap = false;
+
+};
+
+RenderPass.prototype = {
+
+	render: function ( renderer, writeBuffer, readBuffer, delta ) {
+
+    this.scene.overrideMaterial = this.overrideMaterial;
+		if ( this.clearColor ) {
+
+			this.oldClearColor.copy( renderer.getClearColor() );
+			this.oldClearAlpha = renderer.getClearAlpha();
+
+			renderer.setClearColor( this.clearColor, this.clearAlpha );
+
+		}
+
+    renderer.render( this.scene, this.camera, readBuffer, this.clear );
+
+
+		if ( this.clearColor ) {
+
+			renderer.setClearColor( this.oldClearColor, this.oldClearAlpha );
+
+		}
+
+    this.scene.overrideMaterial = null;
+
+	}
+
+};
+
+export default RenderPass;
diff --git a/src/core/engine/renderpass.js b/src/core/engine/renderpass.js
deleted file mode 100644
index f6d3f0c0df..0000000000
--- a/src/core/engine/renderpass.js
+++ /dev/null
@@ -1,45 +0,0 @@
-import * as THREE from '../three';
-
-export default class RenderPass {
-  constructor(cfg) {
-    this.scene;
-    this.camera = cfg.camera;
-    this.renderer = cfg.renderer;
-    this.clearColor = cfg.clear.clearColor;
-    this.clearAlpha = cfg.clear.clearAlpha;
-    this.size = cfg.size ? cfg.size : cfg.renderer.getSize();
-    const defaultRenderCfg = {
-      minFilter: THREE.NearestFilter,
-      magFilter: THREE.NearestFilter,
-      format: THREE.RGBAFormat,
-      stencilBuffer: false,
-      depthBuffer: false
-    };
-    this.renderCfg = cfg.renderCfg ? cfg.renderCfg : defaultRenderCfg;
-    this._init(cfg);
-  }
-
-  _init() {
-    this.scene = new THREE.Scene();
-    this.pass = new THREE.WebGLRenderTarget(this.size.width, this.size.height, this.renderCfg);
-    this.originClearColor = this.renderer.getClearColor();
-    this.originClearAlpha = this.renderer.getClearAlpha();
-    this.texture = this.pass.texture;
-  }
-
-  add(mesh) {
-    this.scene.add(mesh);
-  }
-
-  remove(mesh) {
-    this.scene.remove(mesh);
-  }
-
-  render() {
-
-    this.renderer.setClearColor(this.clearColor, this.clearAlpha);
-    this.renderer.render(this.scene, this.camera, this.pass, true);
-    this.renderer.setRenderTarget(null);
-    this.renderer.setClearColor(this.originClearColor, this.originClearAlpha);
-  }
-}
diff --git a/src/core/engine/shader-pass.js b/src/core/engine/shader-pass.js
new file mode 100644
index 0000000000..6e91880e81
--- /dev/null
+++ b/src/core/engine/shader-pass.js
@@ -0,0 +1,74 @@
+// jscs:disable
+/* eslint-disable */
+
+import * as THREE from '../three';
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+var ShaderPass = function( shader, textureID ) {
+
+	this.textureID = ( textureID !== undefined ) ? textureID : "tDiffuse";
+
+	if ( shader instanceof THREE.ShaderMaterial ) {
+
+		this.uniforms = shader.uniforms;
+
+		this.material = shader;
+
+	}
+	else if ( shader ) {
+
+		this.uniforms = THREE.UniformsUtils.clone( shader.uniforms );
+
+		this.material = new THREE.ShaderMaterial( {
+
+			defines: shader.defines || {},
+			uniforms: this.uniforms,
+			vertexShader: shader.vertexShader,
+			fragmentShader: shader.fragmentShader
+
+		} );
+
+	}
+
+	this.renderToScreen = false;
+
+	this.enabled = true;
+	this.needsSwap = true;
+	this.clear = true;
+
+
+	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.scene.add( this.quad );
+
+};
+
+ShaderPass.prototype = {
+
+	render: function( renderer, writeBuffer, readBuffer, delta ) {
+		if ( this.uniforms[ this.textureID ] ) {
+			this.uniforms[ this.textureID ].value = readBuffer.texture;
+
+		}
+    renderer.autoClear = false;
+		this.quad.material = this.material;
+
+		if ( this.renderToScreen ) {
+			renderer.render( this.scene, this.camera );
+
+		} else {
+
+			renderer.render( this.scene, this.camera, writeBuffer, this.clear );
+
+		}
+		renderer.autoClear = true;
+	}
+
+};
+
+export default ShaderPass;
diff --git a/src/core/layer.js b/src/core/layer.js
index a8b101b97d..4852e0afb7 100644
--- a/src/core/layer.js
+++ b/src/core/layer.js
@@ -82,8 +82,14 @@ export default class Layer extends Base {
     * @param {*} type mesh类型是区别是填充还是边线
    */
   add(object, type = 'fill') {
-    type === 'fill' ? this.layerMesh = object : this.layerLineMesh = object;
+     // composer合图层绘制
+    if (object.type === 'composer') {
+      this._object3D = object;
+      this.scene._engine.composerLayers.push(object);
+      return;
+    }
 
+    type === 'fill' ? this.layerMesh = object : this.layerLineMesh = object;
     this._visibleWithZoom();
     this._zoomchangeHander = this._visibleWithZoom.bind(this);
     this.scene.on('zoomchange', this._zoomchangeHander);
@@ -108,6 +114,12 @@ export default class Layer extends Base {
     }
   }
   remove(object) {
+    if (object.type === 'composer') {
+      this.scene._engine.composerLayers = this.scene._engine.composerLayers.filter(layer => {
+        return (layer !== object);
+      });
+      return;
+    }
     this._object3D.remove(object);
   }
   _getUniqueId() {
@@ -650,6 +662,11 @@ export default class Layer extends Base {
    */
   destroy() {
     this.removeAllListeners();
+    if (this._object3D.type === 'composer') {
+      this.remove(this._object3D);
+
+      return;
+    }
     if (this._object3D && this._object3D.children) {
       let child;
       for (let i = 0; i < this._object3D.children.length; i++) {
diff --git a/src/core/source.js b/src/core/source.js
index 1a3847dfa5..305e535178 100644
--- a/src/core/source.js
+++ b/src/core/source.js
@@ -47,6 +47,10 @@ export default class Source extends Base {
     });
     this._transforms = trans;
   }
+  transform(option) {
+    const data = getTransform(option.type)(this.data, option);
+    Object.assign(this.data, data);
+  }
   _projectCoords() {
     this.data.dataArray.forEach(data => {
       // data.coordinates = this._coordProject(data.coordinates);
diff --git a/src/core/three.js b/src/core/three.js
index a2e468cafd..f832dfbf66 100644
--- a/src/core/three.js
+++ b/src/core/three.js
@@ -18,6 +18,7 @@ export { InstancedBufferGeometry } from 'three/src/core/InstancedBufferGeometry'
 export { PlaneBufferGeometry } from 'three/src/geometries/PlaneGeometry.js';
 export { BoxBufferGeometry } from 'three/src/geometries/BoxGeometry.js';
 export { Raycaster } from 'three/src/core/Raycaster.js';
+export { UniformsUtils } from 'three/src/renderers/shaders/UniformsUtils.js';
 export { Matrix4 } from 'three/src/math/Matrix4.js';
 export { Matrix3 } from 'three/src/math/Matrix3.js';
 export { Line } from 'three/src/objects/Line.js';
diff --git a/src/geom/material/heatmapMateial.js b/src/geom/material/heatmapMateial.js
index 5c0aa893a5..315d19c7b9 100644
--- a/src/geom/material/heatmapMateial.js
+++ b/src/geom/material/heatmapMateial.js
@@ -13,6 +13,7 @@ export function HeatmapIntensityMaterial(opt) {
     vertexShader: vs,
     fragmentShader: fs,
     transparent: true,
+    depthTest: false,
     blending: THREE.AdditiveBlending
   });
   return material;
@@ -23,7 +24,8 @@ export function HeatmapColorizeMaterial(opt) {
   const material = new Material({
     uniforms: {
       u_texture: { value: opt.texture },
-      u_colorRamp: { value: opt.colorRamp }
+      u_colorRamp: { value: opt.colorRamp },
+      u_opacity: { value: opt.opacity }
     },
     vertexShader: vs,
     fragmentShader: fs,
diff --git a/src/geom/shader/heatmap_colorize_frag.glsl b/src/geom/shader/heatmap_colorize_frag.glsl
index cf531398db..4cd75f67ed 100644
--- a/src/geom/shader/heatmap_colorize_frag.glsl
+++ b/src/geom/shader/heatmap_colorize_frag.glsl
@@ -1,11 +1,12 @@
 uniform sampler2D u_texture;
 uniform sampler2D u_colorRamp;
+  uniform float u_opacity;
 varying vec2 v_uv;
 
 void main(){
     float intensity = texture2D(u_texture,v_uv).r;
     vec4 color = texture2D(u_colorRamp,vec2(0.5,1.0-intensity));
     gl_FragColor = color;
-    gl_FragColor.a = color.a * smoothstep(0.,0.05,intensity);
+    gl_FragColor.a = color.a * smoothstep(0.,0.05,intensity) * u_opacity;
     
 }
\ No newline at end of file
diff --git a/src/layer/heatmapLayer.js b/src/layer/heatmapLayer.js
index 0a6931bfb2..ae8e34b1df 100644
--- a/src/layer/heatmapLayer.js
+++ b/src/layer/heatmapLayer.js
@@ -2,7 +2,7 @@ import Layer from '../core/layer';
 import gridBuffer from '../geom/buffer/heatmap/grid';
 import DrawGrid from './render/heatmap/gird';
 import DrawHexagon from './render/heatmap/hexagon';
-import { drawHeatmap, updateIntensityPass } from './render/heatmap/heatmap';
+import { drawHeatmap } from './render/heatmap/heatmap';
 import hexagonBuffer from '../geom/buffer/heatmap/hexagon';
 
 export default class HeatMapLayer extends Layer {
@@ -56,10 +56,10 @@ export default class HeatMapLayer extends Layer {
     this.add(girdMesh);
   }
 
-  afterRender() {
-    if (this.shapeType !== 'grid' && this.shapeType !== 'hexagon') {
-      updateIntensityPass(this);
-    }
-  }
+  // afterRender() {
+  //   if (this.shapeType !== 'grid' && this.shapeType !== 'hexagon') {
+  //     updateIntensityPass(this);
+  //   }
+  // }
 
 }
diff --git a/src/layer/render/heatmap/heatmap.js b/src/layer/render/heatmap/heatmap.js
index 87a5322b7e..b035f38942 100644
--- a/src/layer/render/heatmap/heatmap.js
+++ b/src/layer/render/heatmap/heatmap.js
@@ -1,21 +1,32 @@
 import HeatmapBuffer from '../../../geom/buffer/heatmap/heatmap';
 import { createColorRamp } from '../../../geom/buffer/heatmap/heatmap';
 import { HeatmapIntensityMaterial, HeatmapColorizeMaterial } from '../../../geom/material/heatmapMateial';
-import Renderpass from '../../../core/engine/renderpass';
+// import Renderpass from '../../../core/engine/renderpass.bak';
+import RenderPass from '../../../core/engine/render-pass';
+import ShaderPass from '../../../core/engine/shader-pass';
+import EffectComposer from '../../../core/engine/effect-composer';
 import * as THREE from '../../../core/three';
 
 export function drawHeatmap(layer) {
-  const bbox = calBoundingBox(layer.layerData);
-  layer.dataBbox = bbox;
+
   const colors = layer.get('styleOptions').rampColors;
   layer.colorRamp = createColorRamp(colors);
-  createIntensityPass(layer, bbox);
-  createColorizePass(layer, bbox);
+  const heatmap = new heatmapPass(layer);
+  const copy = new copyPass(layer);
+  copy.renderToScreen = true;
+  const composer = new EffectComposer(layer.scene._engine._renderer, layer.scene._container);
+  composer.addPass(heatmap);
+  composer.addPass(copy);
+  layer.add(composer);
+
 }
 
-function createIntensityPass(layer, bbox) {
+
+function heatmapPass(layer) {
+  const scene = new THREE.Scene();
   const style = layer.get('styleOptions');
   const data = layer.layerData;
+  const camera = layer.scene._engine._camera;
     // get attributes data
   const buffer = new HeatmapBuffer({
     data
@@ -27,107 +38,26 @@ function createIntensityPass(layer, bbox) {
   geometry.addAttribute('position', new THREE.Float32BufferAttribute(attributes.vertices, 3));
   geometry.addAttribute('a_dir', new THREE.Float32BufferAttribute(attributes.dirs, 2));
   geometry.addAttribute('a_weight', new THREE.Float32BufferAttribute(attributes.weights, 1));
-    // set material
   const material = new HeatmapIntensityMaterial({
     intensity: style.intensity,
     radius: style.radius,
     zoom: layer.scene.getZoom()
   });
   const mesh = new THREE.Mesh(geometry, material);
-  // set camera
-  const passOrth = new THREE.OrthographicCamera(bbox.width / -2, bbox.width / 2, bbox.height / 2, bbox.height / -2, 1, 10000);
-  passOrth.position.set(bbox.minX + bbox.width / 2, bbox.minY + bbox.height / 2, 1000);
-  // renderpass
-  const renderer = layer.scene._engine._renderer;
-  // get extension for bilinear texture interpolation:https://threejs.org/docs/#api/en/textures/DataTexture
-  /* const gl = renderer.domElement.getContext('webgl') ||
-    renderer.domElement.getContext('experimental-webgl');
-  gl.getExtension('OES_texture_float_linear');*/
-  const renderpass = new Renderpass({
-    renderer,
-    camera: passOrth,
-    size: {
-      width: 2000,
-      height: 2000 * (bbox.height / bbox.width)
-    },
-    clear: {
-      clearColor: 0x000000,
-      clearAlpha: 0.0
-    },
-    renderCfg: {
-      wrapS: THREE.ClampToEdgeWrapping,
-      wrapT: THREE.ClampToEdgeWrapping,
-      minFilter: THREE.LinearFilter,
-      magFilter: THREE.LinearFilter,
-      format: THREE.RGBAFormat,
-      stencilBuffer: false,
-      depthBuffer: false
-    }
-  });
-  renderpass.add(mesh);
-  renderpass.render();
-  layer.intensityPass = renderpass;
-  layer.intensityMesh = mesh;
-  updateIntensityPass(layer);
-}
-
-export function updateIntensityPass(layer) {
-  const mesh = layer.intensityMesh;
-  const zoom = layer.scene.getZoom();
-  const bbox = layer.dataBbox;
-  mesh.material.uniforms.u_zoom.value = zoom;
-  const passWidth = Math.min(8000, Math.pow(zoom, 2.0) * 250);
-  const passHeight = passWidth * (bbox.height / bbox.width);
-  layer.intensityPass.pass.setSize(passWidth, passHeight);
-  layer.intensityPass.render();
-}
-
-function createColorizePass(layer, bbox) {
-    // create plane geometry
-  const geometery = new THREE.PlaneBufferGeometry(bbox.width, bbox.height);
-  const material = new HeatmapColorizeMaterial({
-    texture: layer.intensityPass.texture,
-    colorRamp: layer.colorRamp
-  });
-  const mesh = new THREE.Mesh(geometery, material);
-  mesh.position.set(bbox.minX + bbox.width / 2, bbox.minY + bbox.height / 2, 0.0);
-  layer.add(mesh);
-}
-
-function calBoundingBox(data) {
-  let minX = Infinity;
-  let minY = Infinity;
-  let maxX = -Infinity;
-  let maxY = -Infinity;
-  for (let i = 0; i < data.length; i++) {
-    const p = data[i].coordinates;
-    if (p[0] < minX) {
-      minX = p[0];
-    } else if (p[0] > maxX) {
-      maxX = p[0];
-    }
-    if (p[1] < minY) {
-      minY = p[1];
-    } else if (p[1] > maxY) {
-      maxY = p[1];
-    }
-  }
-
-  minX -= ((maxX - minX) * 0.5);
-  maxX += ((maxX - minX) * 0.5);
-  minY -= ((maxY - minY) * 0.5);
-  maxY += ((maxY - minY) * 0.5);
-
-  const width = maxX - minX;
-  const height = maxY - minY;
-
-
-  return {
-    minX,
-    maxX,
-    minY,
-    maxY,
-    width,
-    height
+  scene.add(mesh);
+  scene.onBeforeRender = () => { // 每次渲染前改变状态
+    const zoom = layer.scene.getZoom();
+    mesh.material.setUniformsValue('u_zoom', zoom);
   };
+  const pass = new RenderPass(scene, camera);
+  return pass;
+}
+function copyPass(layer) {
+  const style = layer.get('styleOptions');
+  const material = new HeatmapColorizeMaterial({
+    colorRamp: layer.colorRamp,
+    opacity: style.opacity
+  });
+  const copyPass = new ShaderPass(material, 'u_texture');
+  return copyPass;
 }
diff --git a/src/map/AMap.js b/src/map/AMap.js
index 0d4daff491..29374ad778 100644
--- a/src/map/AMap.js
+++ b/src/map/AMap.js
@@ -55,8 +55,6 @@ export default class GaodeMap extends Base {
   asyncCamera(engine) {
     this._engine = engine;
     const camera = engine._camera;
-    const scene = engine._scene;
-    const pickScene = engine._picking._pickingScene;
     this.map.on('camerachange', e => {
       const mapCamera = e.camera;
       let { fov, near, far, height, pitch, rotation, aspect } = mapCamera;
@@ -70,15 +68,16 @@ export default class GaodeMap extends Base {
       camera.position.z = height * Math.cos(pitch);
       camera.position.x = height * Math.sin(pitch) * Math.sin(rotation);
       camera.position.y = -height * Math.sin(pitch) * Math.cos(rotation);
-
       camera.up.x = -Math.cos(pitch) * Math.sin(rotation);
       camera.up.y = Math.cos(pitch) * Math.cos(rotation);
       camera.up.z = Math.sin(pitch);
       camera.lookAt(0, 0, 0);
-      scene.position.x = -e.camera.position.x;
-      scene.position.y = e.camera.position.y;
-      pickScene.position.x = -e.camera.position.x;
-      pickScene.position.y = e.camera.position.y;
+      camera.position.x += e.camera.position.x;
+      camera.position.y += -e.camera.position.y;
+      // scene.position.x = -e.camera.position.x;
+      // scene.position.y = e.camera.position.y;
+      // pickScene.position.x = -e.camera.position.x;
+      // pickScene.position.y = e.camera.position.y;
     });
   }
 
@@ -129,6 +128,10 @@ export default class GaodeMap extends Base {
     scene.setZoom = zoom => {
       return map.setZoom(zoom);
     };
+    scene.setZoomAndCenter = (zoom, center) => {
+      const lnglat = new AMap.LngLat(center[0], center[1]);
+      return map.setZoomAndCenter(zoom, lnglat);
+    };
     scene.setBounds = extent => {
       return map.setBounds(new AMap.Bounds([ extent[0], extent[1] ], [ extent[2], extent[3] ]));
     };
diff --git a/src/source/transform/cluster.js b/src/source/transform/cluster.js
new file mode 100644
index 0000000000..645c7abcc0
--- /dev/null
+++ b/src/source/transform/cluster.js
@@ -0,0 +1,52 @@
+import Supercluster from 'supercluster';
+export function cluster(data, option) {
+  const { radius = 40, maxZoom = 16, minZoom = 0, field, zoom = 2 } = option;
+  if (data.pointIndex) {
+    const clusterPoint = data.pointIndex.getClusters(data.extent, zoom);
+    data.dataArray = formatData(clusterPoint);
+    return data;
+  }
+  const pointIndex = new Supercluster({
+    radius,
+    minZoom,
+    maxZoom,
+    map: props => ({ sum: props[field] }),
+    reduce: (accumulated, props) => { accumulated.sum += props.sum; }
+  });
+  const geojson = {
+    type: 'FeatureCollection'
+  };
+  geojson.features = data.dataArray.map(item => {
+    return {
+      type: 'Feature',
+      properties: {
+        [field]: item[field]
+      },
+      geometry: {
+        type: 'Point',
+        coordinates: item.coordinates
+      }
+    };
+  });
+  pointIndex.load(geojson.features);
+  const clusterPoint = pointIndex.getClusters(data.extent, zoom);
+  const resultData = clusterPoint.map((point, index) => {
+    return {
+      coordinates: point.geometry.coordinates,
+      _id: index + 1,
+      ...point.properties
+    };
+  });
+  data.dataArray = resultData;
+  data.pointIndex = pointIndex;
+  return data;
+}
+function formatData(clusterPoint) {
+  return clusterPoint.map((point, index) => {
+    return {
+      coordinates: point.geometry.coordinates,
+      _id: index + 1,
+      ...point.properties
+    };
+  });
+}
diff --git a/test/unit/source/transfrom/cluster-spec.js b/test/unit/source/transfrom/cluster-spec.js
new file mode 100644
index 0000000000..f46ed7c23a
--- /dev/null
+++ b/test/unit/source/transfrom/cluster-spec.js
@@ -0,0 +1,23 @@
+import { expect } from 'chai';
+import { pointData } from '../../../asset/data/point';
+import { cluster } from '../../../../src/source/transform/cluster';
+describe('hexagon  Test', function() {
+
+  it('pointToCuster', function() {
+    const dataArray = pointData.map(item => {
+      const lng = 1e-6 * (250 * item.grid_x + 125),
+        lat = 1e-6 * (250 * item.grid_y + 125);
+      return {
+        v: item.count * 1,
+        coordinates: [ lng, lat ]
+      };
+    });
+
+    const data = {
+      dataArray,
+      extent: [ -180, -85, 180, 85 ]
+    };
+    const grid = cluster(data, { radius: 40, field: 'v', zoom: 13 });
+    expect(grid.dataArray.length).eql(26);
+  });
+});