refactor(heatmap): composer heatmap

This commit is contained in:
thinkinggis 2019-03-25 19:59:59 +08:00
parent 4739377935
commit d08c47b3d9
14 changed files with 128 additions and 180 deletions

View File

@ -1,7 +1,7 @@
// jscs:disable // jscs:disable
/* eslint-disable */ /* eslint-disable */
import THREE from 'three'; import * as THREE from '../three';
/** /**
* @author alteredq / http://alteredqualia.com/ * @author alteredq / http://alteredqualia.com/
@ -51,4 +51,3 @@ var CopyShader = {
}; };
export default CopyShader; export default CopyShader;
THREE.CopyShader = CopyShader;

View File

@ -1,7 +1,7 @@
// jscs:disable // jscs:disable
/* eslint-disable */ /* eslint-disable */
import THREE from 'three'; import * as THREE from '../three';
/** /**
* @author alteredq / http://alteredqualia.com/ * @author alteredq / http://alteredqualia.com/
@ -92,6 +92,3 @@ ClearMaskPass.prototype = {
export default MaskPass; export default MaskPass;
export {ClearMaskPass as ClearMaskPass}; export {ClearMaskPass as ClearMaskPass};
THREE.MaskPass = MaskPass;
THREE.ClearMaskPass = ClearMaskPass;

View File

@ -1,7 +1,7 @@
// jscs:disable // jscs:disable
/* eslint-disable */ /* eslint-disable */
import THREE from 'three'; import * as THREE from '../three';
/** /**
* @author alteredq / http://alteredqualia.com/ * @author alteredq / http://alteredqualia.com/
@ -37,7 +37,7 @@ var ShaderPass = function( shader, textureID ) {
this.enabled = true; this.enabled = true;
this.needsSwap = true; this.needsSwap = true;
this.clear = false; this.clear = true;
this.camera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 ); this.camera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
@ -51,17 +51,14 @@ var ShaderPass = function( shader, textureID ) {
ShaderPass.prototype = { ShaderPass.prototype = {
render: function( renderer, writeBuffer, readBuffer, delta ) { render: function( renderer, writeBuffer, readBuffer, delta ) {
if ( this.uniforms[ this.textureID ] ) { if ( this.uniforms[ this.textureID ] ) {
this.uniforms[ this.textureID ].value = readBuffer.texture;
this.uniforms[ this.textureID ].value = readBuffer;
} }
renderer.autoClear = false;
this.quad.material = this.material; this.quad.material = this.material;
if ( this.renderToScreen ) { if ( this.renderToScreen ) {
renderer.render( this.scene, this.camera ); renderer.render( this.scene, this.camera );
} else { } else {
@ -69,10 +66,9 @@ ShaderPass.prototype = {
renderer.render( this.scene, this.camera, writeBuffer, this.clear ); renderer.render( this.scene, this.camera, writeBuffer, this.clear );
} }
renderer.autoClear = true;
} }
}; };
export default ShaderPass; export default ShaderPass;
THREE.ShaderPass = ShaderPass;

View File

@ -1,7 +1,7 @@
// jscs:disable // jscs:disable
/* eslint-disable */ /* eslint-disable */
import THREE from 'three'; import * as THREE from '../three';
import CopyShader from './CopyShader'; import CopyShader from './CopyShader';
import ShaderPass from './ShaderPass'; import ShaderPass from './ShaderPass';
import MaskPass, {ClearMaskPass} from './MaskPass'; import MaskPass, {ClearMaskPass} from './MaskPass';
@ -50,7 +50,8 @@ EffectComposer.prototype = {
this.writeBuffer = tmp; this.writeBuffer = tmp;
}, },
visible:true,
type:'composer',
addPass: function ( pass ) { addPass: function ( pass ) {
this.passes.push( pass ); this.passes.push( pass );
@ -71,7 +72,6 @@ EffectComposer.prototype = {
var maskActive = false; var maskActive = false;
var pass, i, il = this.passes.length; var pass, i, il = this.passes.length;
for ( i = 0; i < il; i ++ ) { for ( i = 0; i < il; i ++ ) {
pass = this.passes[ i ]; pass = this.passes[ i ];
@ -147,4 +147,3 @@ EffectComposer.prototype = {
}; };
export default EffectComposer; export default EffectComposer;
THREE.EffectComposer = EffectComposer;

View File

@ -16,14 +16,16 @@ export default class Engine extends EventEmitter {
this._scene.add(this.world); this._scene.add(this.world);
this._picking = Picking(this._world, this._renderer, this._camera, this._scene); this._picking = Picking(this._world, this._renderer, this._camera, this._scene);
this.clock = new THREE.Clock(); this.clock = new THREE.Clock();
this.composerLayers = [];
} }
_initPostProcessing() { _initPostProcessing() {
this.composerLayers.forEach(layer => {
layer.visible && layer.render();
});
} }
update() { update() {
this._renderer.render(this._scene, this._camera); this._renderer.render(this._scene, this._camera);
this._initPostProcessing();
} }
destroy() { destroy() {

View File

@ -1,45 +1,61 @@
// jscs:disable
/* eslint-disable */
import * as THREE from '../three'; import * as THREE from '../three';
export default class RenderPass { /**
constructor(cfg) { * @author alteredq / http://alteredqualia.com/
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() { var RenderPass = function ( scene, camera, overrideMaterial, clearColor, clearAlpha ) {
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 = scene;
this.scene.add(mesh); this.camera = camera;
}
remove(mesh) { this.overrideMaterial = overrideMaterial;
this.scene.remove(mesh);
}
render() { this.clearColor = clearColor;
this.clearAlpha = ( clearAlpha !== undefined ) ? clearAlpha : 1;
this.renderer.setClearColor(this.clearColor, this.clearAlpha); this.oldClearColor = new THREE.Color();
this.renderer.render(this.scene, this.camera, this.pass, true); this.oldClearAlpha = 1;
this.renderer.setRenderTarget(null);
this.renderer.setClearColor(this.originClearColor, this.originClearAlpha); this.enabled = true;
} this.clear = false;
} this.needsSwap = false;
};
RenderPass.prototype = {
render: function ( renderer, writeBuffer, readBuffer, delta ) {
this.scene.overrideMaterial = this.overrideMaterial;
const oldAutoClear = renderer.autoClear;
// renderer.autoClear = false;
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.clear)renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil );
if ( this.clearColor ) {
renderer.setClearColor( this.oldClearColor, this.oldClearAlpha );
}
this.scene.overrideMaterial = null;
renderer.autoClear = oldAutoClear;
}
};
export default RenderPass;

View File

@ -79,8 +79,14 @@ export default class Layer extends Base {
* @param {*} type mesh类型是区别是填充还是边线 * @param {*} type mesh类型是区别是填充还是边线
*/ */
add(object, type = 'fill') { 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._visibleWithZoom();
this._zoomchangeHander = this._visibleWithZoom.bind(this); this._zoomchangeHander = this._visibleWithZoom.bind(this);
this.scene.on('zoomchange', this._zoomchangeHander); this.scene.on('zoomchange', this._zoomchangeHander);
@ -105,6 +111,12 @@ export default class Layer extends Base {
} }
} }
remove(object) { remove(object) {
if (object.type === 'composer') {
this.scene._engine.composerLayers = this.scene._engine.composerLayers.filter(layer => {
return (layer !== object);
});
return;
}
this._object3D.remove(object); this._object3D.remove(object);
} }
_getUniqueId() { _getUniqueId() {
@ -601,6 +613,11 @@ export default class Layer extends Base {
*/ */
destroy() { destroy() {
this.removeAllListeners(); this.removeAllListeners();
if (this._object3D.type === 'composer') {
this.remove(this._object3D);
return;
}
if (this._object3D && this._object3D.children) { if (this._object3D && this._object3D.children) {
let child; let child;
for (let i = 0; i < this._object3D.children.length; i++) { for (let i = 0; i < this._object3D.children.length; i++) {

View File

@ -58,7 +58,6 @@ export default class Source extends Base {
createScale(field) { createScale(field) {
const data = this.data.dataArray; const data = this.data.dataArray;
const scales = this.get('scales'); const scales = this.get('scales');
console.log(scales);
let scale = scales[field]; let scale = scales[field];
const scaleController = this.get('scaleController'); const scaleController = this.get('scaleController');
if (!scale) { if (!scale) {

View File

@ -16,6 +16,7 @@ export { OrthographicCamera } from 'three/src/cameras/OrthographicCamera.js';
export { BufferGeometry } from 'three/src/core/BufferGeometry.js'; export { BufferGeometry } from 'three/src/core/BufferGeometry.js';
export { PlaneBufferGeometry } from 'three/src/geometries/PlaneGeometry.js'; export { PlaneBufferGeometry } from 'three/src/geometries/PlaneGeometry.js';
export { Raycaster } from 'three/src/core/Raycaster.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 { Matrix4 } from 'three/src/math/Matrix4.js';
export { Matrix3 } from 'three/src/math/Matrix3.js'; export { Matrix3 } from 'three/src/math/Matrix3.js';
export { Line } from 'three/src/objects/Line.js'; export { Line } from 'three/src/objects/Line.js';

View File

@ -13,6 +13,7 @@ export function HeatmapIntensityMaterial(opt) {
vertexShader: vs, vertexShader: vs,
fragmentShader: fs, fragmentShader: fs,
transparent: true, transparent: true,
depthTest: false,
blending: THREE.AdditiveBlending blending: THREE.AdditiveBlending
}); });
return material; return material;

View File

@ -2,7 +2,7 @@ import Layer from '../core/layer';
import gridBuffer from '../geom/buffer/heatmap/grid'; import gridBuffer from '../geom/buffer/heatmap/grid';
import DrawGrid from './render/heatmap/gird'; import DrawGrid from './render/heatmap/gird';
import DrawHexagon from './render/heatmap/hexagon'; 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'; import hexagonBuffer from '../geom/buffer/heatmap/hexagon';
export default class HeatMapLayer extends Layer { export default class HeatMapLayer extends Layer {
@ -56,10 +56,10 @@ export default class HeatMapLayer extends Layer {
this.add(girdMesh); this.add(girdMesh);
} }
afterRender() { // afterRender() {
if (this.shapeType !== 'grid' && this.shapeType !== 'hexagon') { // if (this.shapeType !== 'grid' && this.shapeType !== 'hexagon') {
updateIntensityPass(this); // updateIntensityPass(this);
} // }
} // }
} }

View File

@ -1,21 +1,32 @@
import HeatmapBuffer from '../../../geom/buffer/heatmap/heatmap'; import HeatmapBuffer from '../../../geom/buffer/heatmap/heatmap';
import { createColorRamp } from '../../../geom/buffer/heatmap/heatmap'; import { createColorRamp } from '../../../geom/buffer/heatmap/heatmap';
import { HeatmapIntensityMaterial, HeatmapColorizeMaterial } from '../../../geom/material/heatmapMateial'; 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/renderPass';
import ShaderPass from '../../../core/engine/ShaderPass';
import EffectComposer from '../../../core/engine/EffectComposer';
import * as THREE from '../../../core/three'; import * as THREE from '../../../core/three';
export function drawHeatmap(layer) { export function drawHeatmap(layer) {
const bbox = calBoundingBox(layer.layerData);
layer.dataBbox = bbox;
const colors = layer.get('styleOptions').rampColors; const colors = layer.get('styleOptions').rampColors;
layer.colorRamp = createColorRamp(colors); layer.colorRamp = createColorRamp(colors);
createIntensityPass(layer, bbox); const heatmap = new heatmapPass(layer);
createColorizePass(layer, bbox); 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 style = layer.get('styleOptions');
const data = layer.layerData; const data = layer.layerData;
const camera = layer.scene._engine._camera;
// get attributes data // get attributes data
const buffer = new HeatmapBuffer({ const buffer = new HeatmapBuffer({
data data
@ -27,109 +38,26 @@ function createIntensityPass(layer, bbox) {
geometry.addAttribute('position', new THREE.Float32BufferAttribute(attributes.vertices, 3)); geometry.addAttribute('position', new THREE.Float32BufferAttribute(attributes.vertices, 3));
geometry.addAttribute('a_dir', new THREE.Float32BufferAttribute(attributes.dirs, 2)); geometry.addAttribute('a_dir', new THREE.Float32BufferAttribute(attributes.dirs, 2));
geometry.addAttribute('a_weight', new THREE.Float32BufferAttribute(attributes.weights, 1)); geometry.addAttribute('a_weight', new THREE.Float32BufferAttribute(attributes.weights, 1));
// set material
const material = new HeatmapIntensityMaterial({ const material = new HeatmapIntensityMaterial({
intensity: style.intensity, intensity: style.intensity,
radius: style.radius, radius: style.radius,
zoom: layer.scene.getZoom() zoom: layer.scene.getZoom()
}); });
const mesh = new THREE.Mesh(geometry, material); const mesh = new THREE.Mesh(geometry, material);
// set camera scene.add(mesh);
const passOrth = new THREE.OrthographicCamera(bbox.width / -2, bbox.width / 2, bbox.height / 2, bbox.height / -2, 1, 10000); scene.onBeforeRender = () => { // 每次渲染前改变状态
passOrth.position.set(bbox.minX + bbox.width / 2, bbox.minY + bbox.height / 2, 1000); const zoom = layer.scene.getZoom();
// renderpass mesh.material.setUniformsValue('u_zoom', zoom);
const renderer = layer.scene._engine._renderer; };
// get extension for bilinear texture interpolation:https://threejs.org/docs/#api/en/textures/DataTexture const pass = new Renderpass(scene, camera);
/* const gl = renderer.domElement.getContext('webgl') || return pass;
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);
} }
function copyPass(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 style = layer.get('styleOptions'); const style = layer.get('styleOptions');
const geometery = new THREE.PlaneBufferGeometry(bbox.width, bbox.height);
const material = new HeatmapColorizeMaterial({ const material = new HeatmapColorizeMaterial({
texture: layer.intensityPass.texture,
colorRamp: layer.colorRamp, colorRamp: layer.colorRamp,
opacity: style.opacity opacity: style.opacity
}); });
const mesh = new THREE.Mesh(geometery, material); const copyPass = new ShaderPass(material, 'u_texture');
mesh.position.set(bbox.minX + bbox.width / 2, bbox.minY + bbox.height / 2, 0.0); return copyPass;
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
};
} }

View File

@ -55,8 +55,6 @@ export default class GaodeMap extends Base {
asyncCamera(engine) { asyncCamera(engine) {
this._engine = engine; this._engine = engine;
const camera = engine._camera; const camera = engine._camera;
const scene = engine._scene;
const pickScene = engine._picking._pickingScene;
this.map.on('camerachange', e => { this.map.on('camerachange', e => {
const mapCamera = e.camera; const mapCamera = e.camera;
let { fov, near, far, height, pitch, rotation, aspect } = mapCamera; 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.z = height * Math.cos(pitch);
camera.position.x = height * Math.sin(pitch) * Math.sin(rotation); camera.position.x = height * Math.sin(pitch) * Math.sin(rotation);
camera.position.y = -height * Math.sin(pitch) * Math.cos(rotation); camera.position.y = -height * Math.sin(pitch) * Math.cos(rotation);
camera.up.x = -Math.cos(pitch) * Math.sin(rotation); camera.up.x = -Math.cos(pitch) * Math.sin(rotation);
camera.up.y = Math.cos(pitch) * Math.cos(rotation); camera.up.y = Math.cos(pitch) * Math.cos(rotation);
camera.up.z = Math.sin(pitch); camera.up.z = Math.sin(pitch);
camera.lookAt(0, 0, 0); camera.lookAt(0, 0, 0);
scene.position.x = -e.camera.position.x; camera.position.x += e.camera.position.x;
scene.position.y = e.camera.position.y; camera.position.y += -e.camera.position.y;
pickScene.position.x = -e.camera.position.x; // scene.position.x = -e.camera.position.x;
pickScene.position.y = e.camera.position.y; // scene.position.y = e.camera.position.y;
// pickScene.position.x = -e.camera.position.x;
// pickScene.position.y = e.camera.position.y;
}); });
} }

View File

@ -17,13 +17,7 @@ describe('hexagon Test', function() {
dataArray, dataArray,
extent: [ -180, -85, 180, 85 ] extent: [ -180, -85, 180, 85 ]
}; };
console.time('cluster');
const grid = cluster(data, { radius: 40, field: 'v', zoom: 13 }); const grid = cluster(data, { radius: 40, field: 'v', zoom: 13 });
console.log(grid);
console.timeEnd('cluster');
expect(grid.dataArray.length).eql(26); expect(grid.dataArray.length).eql(26);
console.time('cluster');
cluster(data, {zoom:14});
console.timeEnd('cluster');
}); });
}); });