From 5e0bac0fe410eb589ecfade7e1f09ebfbf5d18a2 Mon Sep 17 00:00:00 2001 From: xiaoiver Date: Wed, 29 May 2019 20:37:23 +0800 Subject: [PATCH] feat(point-layer): vertex compression --- src/geom/buffer/point/circleBuffer.js | 49 +++++++++++++++++------- src/geom/shader/circle_vert.glsl | 42 ++++++++++++++------ src/geom/shader/index.js | 3 ++ src/geom/shader/shaderChunks/decode.glsl | 21 ++++++++++ src/layer/render/point/drawCircle.js | 7 +--- src/util/vertex-compress.js | 13 +++++++ 6 files changed, 105 insertions(+), 30 deletions(-) create mode 100644 src/geom/shader/shaderChunks/decode.glsl create mode 100644 src/util/vertex-compress.js diff --git a/src/geom/buffer/point/circleBuffer.js b/src/geom/buffer/point/circleBuffer.js index 094f0c522f..71e29e2016 100644 --- a/src/geom/buffer/point/circleBuffer.js +++ b/src/geom/buffer/point/circleBuffer.js @@ -1,25 +1,48 @@ +import { packUint8ToFloat } from '../../../util/vertex-compress'; + +// const LEFT_SHIFT2 = 4.0; +// const LEFT_SHIFT4 = 16.0; +const LEFT_SHIFT8 = 256.0; +const LEFT_SHIFT10 = 1024.0; +// const LEFT_SHIFT12 = 4096.0; + export default function circleBuffer(layerData) { const index = []; - const aExtrude = []; - const aRadius = []; - const aColor = []; - const aPickingId = []; const aPosition = []; + const aPackedData = []; layerData.forEach(({ size = 0, color, id, coordinates }, i) => { + + if (isNaN(size)) { + size = 0; + } + + // pack color(vec4) into vec2 + const packedColor = [ + packUint8ToFloat(color[0] * 255, color[1] * 255), + packUint8ToFloat(color[2] * 255, color[3] * 255) + ]; + // 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); + [ + [ -1, -1 ], + [ 1, -1 ], + [ 1, 1 ], + [ -1, 1 ] + ].forEach(extrude => { + // vec4(color, color, (8-bit size, 4-bit extrude), id) + aPackedData.push( + ...packedColor, + size + (extrude[0] + 1) * LEFT_SHIFT8 + (extrude[1] + 1) * LEFT_SHIFT10, + 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 + index, + aPackedData }; } diff --git a/src/geom/shader/circle_vert.glsl b/src/geom/shader/circle_vert.glsl index 8783f6bbc9..1644b3bd97 100644 --- a/src/geom/shader/circle_vert.glsl +++ b/src/geom/shader/circle_vert.glsl @@ -1,6 +1,4 @@ -attribute float a_radius; -attribute vec2 a_shape; -attribute vec4 a_color; +attribute vec4 a_packed_data; uniform float u_zoom : 1; uniform float u_stroke_width : 2; @@ -11,24 +9,44 @@ varying vec3 v_data; varying vec4 v_color; varying float v_radius; -void main() { - v_color = a_color; - v_radius = a_radius; +#pragma include "decode" + +void main() { + // unpack color(vec2) + v_color = decode_color(a_packed_data.xy); + // unpack picking_id + float picking_id = a_packed_data.w; + + // unpack data(radius(8-bit), extrude(4-bit)) + float compressed = a_packed_data.z; + + // extrude(4-bit) + vec2 extrude; + extrude.y = floor(compressed * SHIFT_RIGHT10); + compressed -= extrude.y * SHIFT_LEFT10; + extrude.y = extrude.y - 1.; + + extrude.x = floor(compressed * SHIFT_RIGHT8); + compressed -= extrude.x * SHIFT_LEFT8; + extrude.x = extrude.x - 1.; + + // radius(8-bit) + float radius = compressed; + v_radius = radius; - // extrude float zoom_scale = pow(2., 20. - u_zoom); - vec2 offset = a_shape * (a_radius + u_stroke_width) * zoom_scale; + vec2 offset = extrude * (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); + float antialiasblur = 1.0 / (radius + u_stroke_width); // construct point coords - v_data = vec3(a_shape, antialiasblur); + v_data = vec3(extrude, antialiasblur); // picking - if(pickingId == u_activeId) { + if(picking_id == u_activeId) { v_color = u_activeColor; } - worldId = id_toPickColor(pickingId); + worldId = id_toPickColor(picking_id); } \ No newline at end of file diff --git a/src/geom/shader/index.js b/src/geom/shader/index.js index e74c00b898..a23f341538 100644 --- a/src/geom/shader/index.js +++ b/src/geom/shader/index.js @@ -51,9 +51,12 @@ import mask_quard_frag from '../shader/tile/mask_quard_frag.glsl'; import common from './common.glsl'; import { registerModule } from '../../util/shaderModule'; import pick_color from './shaderChunks/pick_color.glsl'; +import decode from './shaderChunks/decode.glsl'; + export function compileBuiltinModules() { registerModule('point', { vs: point_vert, fs: point_frag }); registerModule('common', { vs: common, fs: common }); + registerModule('decode', { vs: decode, fs: '' }); 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 }); diff --git a/src/geom/shader/shaderChunks/decode.glsl b/src/geom/shader/shaderChunks/decode.glsl new file mode 100644 index 0000000000..f1c31420de --- /dev/null +++ b/src/geom/shader/shaderChunks/decode.glsl @@ -0,0 +1,21 @@ +#define SHIFT_RIGHT2 0.25 +#define SHIFT_RIGHT4 0.0625 +#define SHIFT_RIGHT8 1.0 / 256.0 +#define SHIFT_RIGHT10 1.0 / 1024.0 +#define SHIFT_LEFT2 4.0 +#define SHIFT_LEFT4 16.0 +#define SHIFT_LEFT8 256.0 +#define SHIFT_LEFT10 1024.0 + +vec2 unpack_float(const float packedValue) { + int packedIntValue = int(packedValue); + int v0 = packedIntValue / 256; + return vec2(v0, packedIntValue - v0 * 256); +} + +vec4 decode_color(const vec2 encodedColor) { + return vec4( + unpack_float(encodedColor[0]) / 255.0, + unpack_float(encodedColor[1]) / 255.0 + ); +} \ No newline at end of file diff --git a/src/layer/render/point/drawCircle.js b/src/layer/render/point/drawCircle.js index 0de9223ef2..6f8df6f48b 100644 --- a/src/layer/render/point/drawCircle.js +++ b/src/layer/render/point/drawCircle.js @@ -9,14 +9,11 @@ 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 { aPosition, aPackedData, index } = 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)); + geometry.addAttribute('a_packed_data', new THREE.Float32BufferAttribute(aPackedData, 4)); const material = new CircleMaterial({ u_opacity: style.opacity, diff --git a/src/util/vertex-compress.js b/src/util/vertex-compress.js new file mode 100644 index 0000000000..075020fb78 --- /dev/null +++ b/src/util/vertex-compress.js @@ -0,0 +1,13 @@ +import clamp from '@antv/util/lib/clamp'; + +/** + * encode 2 8-bit unsigned int into a 16-bit float + * @param {number} a 8-bit int + * @param {number} b 8-bit int + * @return {number} float + */ +export function packUint8ToFloat(a, b) { + a = clamp(Math.floor(a), 0, 255); + b = clamp(Math.floor(b), 0, 255); + return 256 * a + b; +}