diff --git a/package.json b/package.json index 723a1e1cf1..023c96727f 100755 --- a/package.json +++ b/package.json @@ -99,6 +99,7 @@ "@antv/geo-coord": "^1.0.8", "@antv/util": "~2.0.1", "@mapbox/tiny-sdf": "^1.1.0", + "@mapbox/vector-tile": "^1.3.1", "@turf/bbox": "^6.0.1", "@turf/clean-coords": "^6.0.1", "@turf/invariant": "^6.1.2", @@ -110,6 +111,7 @@ "gl-matrix": "^2.4.1", "gl-vec2": "^1.3.0", "lodash": "^4.17.5", + "pbf": "^3.2.0", "polyline-miter-util": "^1.0.1", "rbush": "^2.0.2", "simple-statistics": "^7.0.1", diff --git a/src/geom/buffer/point/circleBuffer.js b/src/geom/buffer/point/circleBuffer.js new file mode 100644 index 0000000000..094f0c522f --- /dev/null +++ b/src/geom/buffer/point/circleBuffer.js @@ -0,0 +1,25 @@ +export default function circleBuffer(layerData) { + const index = []; + const aExtrude = []; + const aRadius = []; + const aColor = []; + const aPickingId = []; + const aPosition = []; + layerData.forEach(({ size = 0, color, id, coordinates }, i) => { + // construct point coords + aExtrude.push(-1, -1, 1, -1, 1, 1, -1, 1); + aRadius.push(size, size, size, size); + aColor.push(...color, ...color, ...color, ...color); + aPickingId.push(id, id, id, id); + aPosition.push(...coordinates, ...coordinates, ...coordinates, ...coordinates); + index.push(...[ 0, 1, 2, 0, 2, 3 ].map(n => n + i * 4)); + }); + return { + aExtrude, + aRadius, + aColor, + aPickingId, + aPosition, + index + }; +} diff --git a/src/geom/buffer/point/index.js b/src/geom/buffer/point/index.js index 80d52c5ff1..cfc406f5bc 100644 --- a/src/geom/buffer/point/index.js +++ b/src/geom/buffer/point/index.js @@ -2,3 +2,4 @@ export { default as FillBuffer } from './fillBuffer'; export { default as StrokeBuffer } from './strokeBuffer'; export { default as ImageBuffer } from './imageBuffer'; export { default as NormalBuffer } from './normalBuffer'; +export { default as CircleBuffer } from './circleBuffer'; diff --git a/src/geom/material/circleMaterial.js b/src/geom/material/circleMaterial.js new file mode 100644 index 0000000000..d042c91e75 --- /dev/null +++ b/src/geom/material/circleMaterial.js @@ -0,0 +1,16 @@ +import Material from './material'; +import { getModule, wrapUniforms } from '../../util/shaderModule'; +import merge from '@antv/util/lib/deep-mix'; + +export default class CircleMaterial extends Material { + constructor(_uniforms, _defines, parameters) { + super(parameters); + const { vs, fs, uniforms } = getModule('circle'); + this.uniforms = wrapUniforms(merge(uniforms, _uniforms)); + this.defines = _defines; + this.type = 'CircleMaterial'; + this.vertexShader = vs; + this.fragmentShader = fs; + this.transparent = true; + } +} diff --git a/src/geom/shader/circle_frag.glsl b/src/geom/shader/circle_frag.glsl new file mode 100644 index 0000000000..db81ca186f --- /dev/null +++ b/src/geom/shader/circle_frag.glsl @@ -0,0 +1,26 @@ +uniform float u_blur : 0; +uniform float u_opacity : 1; +uniform float u_stroke_width : 1; +uniform vec4 u_stroke_color : [1, 1, 1, 1]; +uniform float u_stroke_opacity : 1; + +varying vec3 v_data; +varying vec4 v_color; +varying float v_radius; + +void main() { + float extrude_length = length(v_data.xy); + + lowp float antialiasblur = v_data.z; + float antialiased_blur = -max(u_blur, antialiasblur); + + float opacity_t = smoothstep(0.0, antialiased_blur, extrude_length - 1.0); + + float color_t = u_stroke_width < 0.01 ? 0.0 : smoothstep( + antialiased_blur, + 0.0, + extrude_length - v_radius / (v_radius + u_stroke_width) + ); + + gl_FragColor = opacity_t * mix(v_color * u_opacity, u_stroke_color * u_stroke_opacity, color_t); +} \ No newline at end of file diff --git a/src/geom/shader/circle_vert.glsl b/src/geom/shader/circle_vert.glsl new file mode 100644 index 0000000000..8783f6bbc9 --- /dev/null +++ b/src/geom/shader/circle_vert.glsl @@ -0,0 +1,34 @@ +attribute float a_radius; +attribute vec2 a_shape; +attribute vec4 a_color; + +uniform float u_zoom : 1; +uniform float u_stroke_width : 2; +uniform float u_activeId : 0; +uniform vec4 u_activeColor : [ 1.0, 0, 0, 1.0 ]; + +varying vec3 v_data; +varying vec4 v_color; +varying float v_radius; + +void main() { + v_color = a_color; + v_radius = a_radius; + + // extrude + float zoom_scale = pow(2., 20. - u_zoom); + vec2 offset = a_shape * (a_radius + u_stroke_width) * zoom_scale; + gl_Position = projectionMatrix * modelViewMatrix * vec4(position.xy + offset, 0.0, 1.0); + + // anti-alias + float antialiasblur = 1.0 / (a_radius + u_stroke_width); + + // construct point coords + v_data = vec3(a_shape, antialiasblur); + + // picking + if(pickingId == u_activeId) { + v_color = u_activeColor; + } + worldId = id_toPickColor(pickingId); +} \ No newline at end of file diff --git a/src/geom/shader/index.js b/src/geom/shader/index.js index e6ff0c5c79..e74c00b898 100644 --- a/src/geom/shader/index.js +++ b/src/geom/shader/index.js @@ -6,6 +6,8 @@ import grid_frag from '../shader/grid_frag.glsl'; import grid_vert from '../shader/grid_vert.glsl'; import hexagon_frag from '../shader/hexagon_frag.glsl'; import hexagon_vert from '../shader/hexagon_vert.glsl'; +import circle_frag from './circle_frag.glsl'; +import circle_vert from './circle_vert.glsl'; // 点的边线 import point_line_frag from '../shader/point_meshLine_frag.glsl'; @@ -53,6 +55,7 @@ export function compileBuiltinModules() { registerModule('point', { vs: point_vert, fs: point_frag }); registerModule('common', { vs: common, fs: common }); registerModule('pick_color', { vs: pick_color, fs: pick_color }); + registerModule('circle', { vs: circle_vert, fs: circle_frag }); registerModule('polygon', { vs: polygon_vert, fs: polygon_frag }); registerModule('grid', { vs: grid_vert, fs: grid_frag }); registerModule('hexagon', { vs: hexagon_vert, fs: hexagon_frag }); diff --git a/src/layer/pointLayer.js b/src/layer/pointLayer.js index 07d915c127..8439ccaf79 100644 --- a/src/layer/pointLayer.js +++ b/src/layer/pointLayer.js @@ -28,6 +28,11 @@ export default class PointLayer extends Layer { break; } } + + // 2D circle 特殊处理 + if (shape === 'circle') { + return 'circle'; + } if ( pointShape['2d'].indexOf(shape) !== -1 || pointShape['3d'].indexOf(shape) !== -1 diff --git a/src/layer/render/index.js b/src/layer/render/index.js index 16a2d48273..5c5e5baddf 100644 --- a/src/layer/render/index.js +++ b/src/layer/render/index.js @@ -22,12 +22,14 @@ import DrawPointImage from './point/drawImage'; import DrawPointNormal from './point/drawNormal'; import DrawPointStroke from './point/drawStroke'; import DrawPointText from './point/drawText'; +import DrawPointCircle from './point/drawCircle'; registerRender('point', 'fill', DrawPointFill); registerRender('point', 'image', DrawPointImage); registerRender('point', 'normal', DrawPointNormal); registerRender('point', 'stroke', DrawPointStroke); registerRender('point', 'text', DrawPointText); +registerRender('point', 'circle', DrawPointCircle); // heatmap diff --git a/src/layer/render/point/drawCircle.js b/src/layer/render/point/drawCircle.js new file mode 100644 index 0000000000..0de9223ef2 --- /dev/null +++ b/src/layer/render/point/drawCircle.js @@ -0,0 +1,32 @@ +/** + * 针对绘制圆形的优化 + * 手动构建点阵坐标系,便于实现描边、反走样效果 + */ +import * as THREE from '../../../core/three'; +import * as PointBuffer from '../../../geom/buffer/point/index'; +import CircleMaterial from '../../../geom/material/circleMaterial'; +export default function drawCircle(layerData, layer) { + const style = layer.get('styleOptions'); + const activeOption = layer.get('activedOptions'); + + const { aColor, aExtrude, aPickingId, aPosition, index, aRadius } = PointBuffer.CircleBuffer(layerData, style); + const geometry = new THREE.BufferGeometry(); + geometry.setIndex(index); + geometry.addAttribute('position', new THREE.Float32BufferAttribute(aPosition, 3)); + geometry.addAttribute('a_color', new THREE.Float32BufferAttribute(aColor, 4)); + geometry.addAttribute('pickingId', new THREE.Float32BufferAttribute(aPickingId, 1)); + geometry.addAttribute('a_shape', new THREE.Float32BufferAttribute(aExtrude, 2)); + geometry.addAttribute('a_radius', new THREE.Float32BufferAttribute(aRadius, 1)); + + const material = new CircleMaterial({ + u_opacity: style.opacity, + u_activeColor: activeOption.fill, + u_zoom: layer.scene.getZoom(), + u_stroke_color: style.stroke, + u_stroke_width: style.strokeWidth, + u_stroke_opacity: style.strokeOpacity + }); + material.depthTest = false; + const fillMesh = new THREE.Mesh(geometry, material); + return fillMesh; +}