From cf326c047cb80d768043e87c1f7ec73b817821d4 Mon Sep 17 00:00:00 2001 From: YiQianYao <42212176+2912401452@users.noreply.github.com> Date: Tue, 8 Mar 2022 18:13:22 +0800 Subject: [PATCH] Shihui (#996) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * docs: add keywords * feat: 升级部分官网 demo,增加 bloom 效果 * feat: 增加 pointLayer fillImage 模式的 demo * style: lint style * docs: 补充后处理相关文档 * style: lint style * docs: 完善图层基础方法 --- docs/api/pass.en.md | 6 + docs/api/pass.zh.md | 348 ++++++++++++++++++ docs/api/point_layer/symbol.zh.md | 47 +++ docs/common/layer/layer_interaction.md | 52 +++ .../gallery/animate/demo/plane_animate.js | 16 +- examples/line/animate/demo/trip_animate.js | 2 +- .../line/animate/demo/trip_animate_dark.js | 13 +- examples/line/isoline/demo/ele_dark.js | 14 +- examples/point/image/demo/fillimage.js | 58 +++ examples/point/image/demo/meta.json | 5 + gatsby-config.js | 9 + 11 files changed, 566 insertions(+), 4 deletions(-) create mode 100644 docs/api/pass.en.md create mode 100644 docs/api/pass.zh.md create mode 100644 examples/point/image/demo/fillimage.js diff --git a/docs/api/pass.en.md b/docs/api/pass.en.md new file mode 100644 index 0000000000..fde558c24b --- /dev/null +++ b/docs/api/pass.en.md @@ -0,0 +1,6 @@ +--- +title: MultiPass +order: 10 +--- + +`markdown:docs/api/pass.zh.md` diff --git a/docs/api/pass.zh.md b/docs/api/pass.zh.md new file mode 100644 index 0000000000..872184e208 --- /dev/null +++ b/docs/api/pass.zh.md @@ -0,0 +1,348 @@ +--- +title: 后处理模块 +order: 10 +--- + +后处理(Post-Process Effect)是 3D 渲染常见的处理效果,是一种对渲染之后的画面进行再加工的技术,一般用于实现各种特效。L7 的后处理模块为用户提供了一些常见的后处理效果,同时也提供了标准规范,允许用户自定义后处理效果。 + +🌟 需要注意的是,使用后处理通常会产生额外的性能消耗,用户应该根据项目的实际情况合理使用后处理。 + +## 使用 + +```jsx +const layer = new LineLayer({ + enableMultiPassRenderer: true, + passes: [ + [ + 'bloom', + { + bloomBaseRadio: 0.8, + bloomRadius: 2, + bloomIntensity: 1 + } + ] + ] +}).source(data) + .size('ELEV', h => [ h % 50 === 0 ? 1.0 : 0.5, (h - 1300) * 0.2 ]) + .shape('line') + .scale('ELEV', { + type: 'quantize' + }) + .color('ELEV', [ + '#094D4A', + ... + ]); +scene.addLayer(layer); +``` + +案例 + +[在线案例](../../examples/line/isoline#ele_dark) + +### 开启后处理 + +为了开启图层的后处理能力,我们需要在初始化图层的时候配置 enableMultiPassRenderer 为 true,同时传入该图层作用的处理效果配置。 + +```javascript +let pointLayer = new PointLayer({ + zIndex: 1, + enableMultiPassRenderer: false, + passes: [ + [ + 'bloom', + { + bloomBaseRadio: 0.95, + bloomRadius: 4, + bloomIntensity: 1.1, + }, + ], + ], +}); +``` + +- enableMultiPassRenderer 配置该图层是否开始后处理能力 +- passes 后处理配置列表 + 🌟 passes 需要根据一定的规则配置 + +### 单图层后处理 + +传统的后处理渲染往往会对场景中所有的对象做统一的后处理,而许多时候我们只需要对场景中的一部分内容做后处理。L7 的后处理模块天然支持以图层为单位进行后处理,这使的用户对 L7 场景内容的处理有更高的自由度。 + +### update pass options + +用户在初始化完图层对象之后,若想调整后处理效果的参数,可以直接使用 style 方法 + +```javascript +layer.style({ + passes: [ + [ + 'colorHalftone', + { + // 更新 cenrter 的位置 + center: [newX, newY], + }, + ], + ], +}); +scene.render(); +``` + +### setMultiPass(enableMultiPassRenderer: boolean, passes?: pass[]) + +为了方便用户切换后处理的状态(开启、关闭后处理),我们为用户提供了专门的方法 + +```javascript +// 当前图层存在 multiPass,我们需要关闭时 +// 直接关闭 +layer.setMultiPass(false); +// 关闭的同时清除 passes +layer.setMultiPass(false, []); + +// 当前图层不存在 multiPass,我们需要开启时 +// 图层初始化时已经传入 passes +const layer = new PolygonLayer({ + zIndex: 0, + enableMultiPassRenderer: false, + passes: [ + [ + 'bloom', + { + bloomBaseRadio: 0.5, + bloomRadius: 20, + bloomIntensity: 1, + }, + ], + ], +}); +layer.setMultiPass(true); + +// 图层初始化时没有传入 passes +layer.setMultiPass(true, [ + [ + 'bloom', + { + bloomRadius: 10, + bloomIntensity: 1, + }, + ], +]); +``` + +### 后处理链路 + +passes 可以传入多种后处理,普通渲染的结果是第一个后处理的输入,前一种后处理的输出是后一个后处理的输入,最后的结果输出到屏幕。 + +### 预制的后处理 + +L7 的后处理模块预置了几种后处理效果,因此用户可以直接在 passes 中配置使用。 + +#### bloom + +案例 + +```javascript +const bloomPass = [ + 'bloom', + { + bloomBaseRadio: 0.5, + bloomRadius: 20, + bloomIntensity: 1, + }, +]; +``` + +辉光后处理 + +- bloomBaseRadio + 设置保持图形原本样式的比例,值在 0 - 1 之间,值为 1 时完全保存本身的样式 +- bloomRadius + 设置 bloom 的半径,值越大,bloom 范围越大 +- bloomIntensity + 设置 bloom 的强度,值越大,辉光越强 + +#### blurV/blurH + +案例 + +垂直方向模糊/水平方向模糊 + +```javascript +const blurVPass = [ + 'blurV', + { + blurRadius: 5, + }, +]; +const blurHPass = [ + 'blurH', + { + blurRadius: 5, + }, +]; +``` + +- blurRadius + 设置模糊半径 + +#### colorHalftone + +案例 + +colorHalftone + +```javascript +const colorHalftonePass = [ + 'colorHalftone', + { + angle: 0, + size: 8, + centerX: 0.5, + centerY: 0.5, + }, +]; +``` + +- angle + 设置角度 +- size + 设置大小 +- centerX + 设置中心点 X +- centerY + 设置中心点 Y + +#### hexagonalPixelate + +案例 + +六边形像素 + +```javascript +const hexagonalPixelatePass = [ + 'hexagonalPixelate', + { + scale: 10, + centerX: 0.5, + centerY: 0.5, + }, +]; +``` + +- scale + 设置缩放 +- centerX + 设置中心点 X +- centerY + 设置中心点 Y + +#### ink + +案例 + +ink + +```javascript +const inkPass = [ + 'ink', + { + strength: 1, + }, +]; +``` + +- strength + 设置强度 + +#### noise + +案例 + +噪声 + +```javascript +const noisePass = [ + 'noise', + { + amount: 1, + }, +]; +``` + +- amount + 设置噪点数量 + +### 自定义后处理 + +用户通过 L7 定义的标准可以轻松的自定义后处理效果。 + +```javascript +import { BasePostProcessingPass, PolygonLayer, Scene } from '@antv/l7'; + +interface IDotScreenEffectConfig { + center: [number, number]; // pattern 圆心 + angle: number; // dot 旋转角度 + size: number; // dot 尺寸 +} + +class DotScreenEffect extends BasePostProcessingPass { + protected setupShaders() { + this.shaderModuleService.registerModule('dotScreenEffect', { + vs: this.quad, + fs: ` + varying vec2 v_UV; + + uniform sampler2D u_Texture; + uniform vec2 u_ViewportSize : [1.0, 1.0]; + uniform vec2 u_Center : [0.5, 0.5]; + uniform float u_Angle : 1; + uniform float u_Size : 3; + + float pattern(vec2 texSize, vec2 texCoord) { + float scale = 3.1415 / u_Size; + float s = sin(u_Angle), c = cos(u_Angle); + vec2 tex = texCoord * texSize - u_Center * texSize; + vec2 point = vec2( + c * tex.x - s * tex.y, + s * tex.x + c * tex.y + ) * scale; + return (sin(point.x) * sin(point.y)) * 4.0; + } + vec4 dotScreen_filterColor(vec4 color, vec2 texSize, vec2 texCoord) { + float average = (color.r + color.g + color.b) / 3.0; + return vec4(vec3(average * 10.0 - 5.0 + pattern(texSize, texCoord)), color.a); + } + + void main() { + gl_FragColor = vec4(texture2D(u_Texture, v_UV)); + gl_FragColor = dotScreen_filterColor(gl_FragColor, u_ViewportSize, v_UV); + } + `, + }); + const { vs, fs, uniforms } = this.shaderModuleService.getModule('dotScreenEffect'); + const { width, height } = this.rendererService.getViewportSize(); + return { + vs, + fs, + uniforms: { + ...uniforms, + u_ViewportSize: [width, height], + }, + }; + } +} + +// 注册自定义后处理效果 +scene.registerPostProcessingPass(DotScreenEffect, 'dotScreenEffect'); +const layer = new PolygonLayer({ + enableMultiPassRenderer: true, + passes: [ + [ + 'dotScreenEffect', + { + size: 8, + angle: 1, + }, + ], + ], +}); +``` diff --git a/docs/api/point_layer/symbol.zh.md b/docs/api/point_layer/symbol.zh.md index 0d9a5f1c9f..43309b7660 100644 --- a/docs/api/point_layer/symbol.zh.md +++ b/docs/api/point_layer/symbol.zh.md @@ -54,4 +54,51 @@ const scatter = new PointLayer() [在线案例](../../../examples/point/image#image) +### layerType = "fillImage" + +🌟 默认通过 PointLayer 实例化的 image 本质上是精灵贴图,因此有始终面向相机的特性,同时贴图的大小也收到设备的限制。 +🌟 由于精灵始终面向相机,因此我们也无法自定义配置 image 的旋转角度 + +为了解决上述的两个问题(1. 大小受限,2. 无法自定义旋转角度),我们单独提供了 fillimage 的模式。 +只需要在初始化图层的时候提前指定 layerType 为 fillImage,其他使用与普通的 image 完全相同。 + +```javascript +const imageLayer = new PointLayer({ layerType: 'fillImage' }) + .source(data, { + parser: { + type: 'json', + x: 'longitude', + y: 'latitude', + }, + }) + .shape('name', ['00', '01', '02']) + .style({ + rotation: 0, + }) + .active({ + color: '#0ff', + mix: 0.5, + }) + .size(45); +scene.addLayer(imageLayer); + +let r = 0; +rotate(); +function rotate() { + r += 0.2; + imageLayer.style({ + rotation: r, + }); + scene.render(); + requestAnimationFrame(rotate); +} +``` + +- rotation: number|undefined + 我们支持使用 rotation 自定义配置图标的旋转角度(顺时针方向、角度制) + +案例 + +[在线案例](../../../examples/point/image#fillimage) + `markdown:docs/common/layer/base.md` diff --git a/docs/common/layer/layer_interaction.md b/docs/common/layer/layer_interaction.md index 39fa4c603a..aa7a81b1f1 100644 --- a/docs/common/layer/layer_interaction.md +++ b/docs/common/layer/layer_interaction.md @@ -78,6 +78,58 @@ layer.select(false); layer.setSelect(featureId); ``` +### setAutoFit(autoFit: boolean) +让用户可以主动设置图层的 autoFit 参数 +🌟 设置完该方法后会在图层发生更新的时候生效,如在 setData 之后触发 + +```javascript +// 使用方法 +layer.setAutoFit(true); +// 内部实现 +public setAutoFit(autoFit: boolean): ILayer { + this.updateLayerConfig({ + autoFit, + }); + return this; + } +``` + +### getScale(attr: string) +支持单独获取某个图形经过 scale 计算后的值, 满足用户获取图层某些 feature 值的需求。 +- attr scale 的属性值 + +```javascript +const data = [ + {lng: 120, lat: 30, name: 'n1'}, + {lng: 120, lat: 30, name: 'n2'} +] +const layer = new PointLayer() + .source(data, { + parser: { + x: 'lng', + y: 'lat', + type: 'json' + } + }) + .shape('circle') + .color('name', ['#f00', '#ff0']) + .size('name', [20, 40]) + +scene.addLayer(layer) + + +// 此时在 scene 上绘制两个点 +// 一个颜色为黄色,大小为 40 的点,对应 name 为 n1 +// 一个颜色为红色,大小为 20 的点,对应 name 为 n2 + +const colorScale = layer.getScale('color'); // 获取 color 方法产生的 scale +const color1 = colorScale('n1'); // '#ff0' +const color1 = colorScale('n2'); // '#f00' + +const sizeScale = layer.getScale('size'); // 获取 size 方法产生的 scale +const size1 = sizeScale('n1'); // 40 +const size2 = sizeScale('n2'); // 20 +``` ### getLegendItems(type: string) 获取图例配置 diff --git a/examples/gallery/animate/demo/plane_animate.js b/examples/gallery/animate/demo/plane_animate.js index 8005dd78f2..2cfaeccdd0 100644 --- a/examples/gallery/animate/demo/plane_animate.js +++ b/examples/gallery/animate/demo/plane_animate.js @@ -91,7 +91,21 @@ scene.on('loaded', () => { .style({ opacity: 1.0 }); - const flyLine = new LineLayer({ blend: 'additive', zIndex: 2 }) + const flyLine = new LineLayer({ + blend: 'additive', + zIndex: 2, + enableMultiPassRenderer: true, + passes: [ + [ + 'bloom', + { + bloomBaseRadio: 0.8, + bloomRadius: 2, + bloomIntensity: 1 + } + ] + ] + }) .source(flydata, { parser: { type: 'json', diff --git a/examples/line/animate/demo/trip_animate.js b/examples/line/animate/demo/trip_animate.js index aac6323c34..f7c053a6b0 100644 --- a/examples/line/animate/demo/trip_animate.js +++ b/examples/line/animate/demo/trip_animate.js @@ -40,7 +40,7 @@ scene.on('loaded', () => { duration: 5 }) .style({ - opacity: 1 + opacity: 0.5 }); scene.addLayer(layer); }); diff --git a/examples/line/animate/demo/trip_animate_dark.js b/examples/line/animate/demo/trip_animate_dark.js index 10590046ba..f1d329d594 100644 --- a/examples/line/animate/demo/trip_animate_dark.js +++ b/examples/line/animate/demo/trip_animate_dark.js @@ -16,7 +16,18 @@ scene.on('loaded', () => { .then(res => res.text()) .then(data => { const layer = new LineLayer({ - blend: 'normal' + blend: 'normal', + enableMultiPassRenderer: true, + passes: [ + [ + 'bloom', + { + bloomBaseRadio: 0.8, + bloomRadius: 2, + bloomIntensity: 1 + } + ] + ] }) .source(data, { parser: { diff --git a/examples/line/isoline/demo/ele_dark.js b/examples/line/isoline/demo/ele_dark.js index cb575b888b..fc7e8a4535 100644 --- a/examples/line/isoline/demo/ele_dark.js +++ b/examples/line/isoline/demo/ele_dark.js @@ -16,7 +16,19 @@ scene.on('loaded', () => { fetch('https://gw.alipayobjects.com/os/rmsportal/ZVfOvhVCzwBkISNsuKCc.json') .then(res => res.json()) .then(data => { - const layer = new LineLayer({}) + const layer = new LineLayer({ + enableMultiPassRenderer: true, + passes: [ + [ + 'bloom', + { + bloomBaseRadio: 0.8, + bloomRadius: 2, + bloomIntensity: 1 + } + ] + ] + }) .source(data) .size('ELEV', h => { return [ h % 50 === 0 ? 1.0 : 0.5, (h - 1300) * 0.2 ]; diff --git a/examples/point/image/demo/fillimage.js b/examples/point/image/demo/fillimage.js new file mode 100644 index 0000000000..6797422a7f --- /dev/null +++ b/examples/point/image/demo/fillimage.js @@ -0,0 +1,58 @@ +import { Scene, PointLayer } from '@antv/l7'; +import { GaodeMap } from '@antv/l7-maps'; + +const scene = new Scene({ + id: 'map', + map: new GaodeMap({ + pitch: 0, + style: 'light', + center: [ 121.434765, 31.256735 ], + zoom: 14.83 + }) +}); +scene.on('loaded', () => { + fetch( + 'https://gw.alipayobjects.com/os/basement_prod/893d1d5f-11d9-45f3-8322-ee9140d288ae.json' + ) + .then(res => res.json()) + .then(data => { + scene.addImage( + '00', + 'https://gw.alipayobjects.com/zos/basement_prod/604b5e7f-309e-40db-b95b-4fac746c5153.svg' + ); + scene.addImage( + '01', + 'https://gw.alipayobjects.com/zos/basement_prod/30580bc9-506f-4438-8c1a-744e082054ec.svg' + ); + scene.addImage( + '02', + 'https://gw.alipayobjects.com/zos/basement_prod/7aa1f460-9f9f-499f-afdf-13424aa26bbf.svg' + ); + const imageLayer = new PointLayer({ layerType: 'fillImage' }) + .source(data, { + parser: { + type: 'json', + x: 'longitude', + y: 'latitude' + } + }) + .shape('name', [ '00', '01', '02' ]) + .active({ + color: '#0ff', + mix: 0.5 + }) + .size(45); + scene.addLayer(imageLayer); + + let r = 0; + rotate(); + function rotate() { + r += 0.2; + imageLayer.style({ + rotation: r + }); + scene.render(); + requestAnimationFrame(rotate); + } + }); +}); diff --git a/examples/point/image/demo/meta.json b/examples/point/image/demo/meta.json index 73fef08ce3..0109a7343e 100644 --- a/examples/point/image/demo/meta.json +++ b/examples/point/image/demo/meta.json @@ -9,6 +9,11 @@ "title": "符号图", "screenshot": "https://gw.alipayobjects.com/mdn/antv_site/afts/img/A*oVyHT5S3sv0AAAAAAAAAAABkARQnAQ" }, + { + "filename": "fillimage.js", + "title": "贴地符号图", + "screenshot": "https://gw.alipayobjects.com/mdn/rms_816329/afts/img/A*1kBZTaains4AAAAAAAAAAAAAARQnAQ" + }, { "filename": "locate.js", "title": "精确符号", diff --git a/gatsby-config.js b/gatsby-config.js index 883ebb32b4..4aff7e7b7d 100644 --- a/gatsby-config.js +++ b/gatsby-config.js @@ -14,6 +14,7 @@ module.exports = { 'Large-scale WebGL-powered Geospatial data visualization analysis framework', siteUrl: 'https://l7.antv.vision', githubUrl: 'https://github.com/antvis/L7', + keywords: 'l7, L7, antv/l7, 地理, 空间可视化, Webgl, webgl, 地图, webgis, 3d, GIS, gis, Mapbox, deckgl, g2, g6, antv,', showChartResize: true, // 是否在demo页展示图表视图切换 showAPIDoc: true, // 是否在demo页展示API文档 navs: [ @@ -229,6 +230,14 @@ module.exports = { }, order: 9, }, + { + slug: 'api/pass', + title: { + zh: '后处理模块', + en: 'MultiPass', + }, + order: 10, + }, { slug: 'api/district', title: {