* feat: 增加 bloomPass1.0、修改渲染流程,让 multiPass 有正确的渲染顺序

* style: lint style

* feat: 取消 bloom 在 postprocessor 中的多次渲染(没有明显优化)

* feat: polygon extrude 模式支持配置固定高度

* style: lint style

* feat: 优化后处理 bloom 的效果

* feat: 修改交替绘制 bloom 的写法

* style: lint style

* feat: 完善 iconService 加载渲染和销毁

* style: lint style

* feat: 补全 mapbox 模式下等面积点

* style: lint style

* fix: 修复 pointLayer animate 模式 opacity 失效

* style: lint style

* feat: 拆分 pointLayer 的 shader

* style: lint sytle

* feat: 拆分 lineLayer 的 linear 模式

* style: lint style

* feat: 优化点击的拾取判断

* style: lint style

* feat: 取消圆柱 shader 中的三元表达式、增强健壮性

* feat: 点图层圆柱体支持固定高度配置 heightfixed

* feat: 点图层圆柱体支持拾取高亮颜色的光照计算

* style: lint style

* style: lint style

* feat: 拆分 lintLayer line 模式下的 dash line

* style: lint style

* feat: lineLayer simpleline 的 linear shader 代码拆分

* style: lint style

* feat: 拆分 lineLayer arcLine linear shader  代码

* style: line style

* feat: lineLayer arc line 在 shader 中移除 linear 部分计算

* feat: 拆分 lineLayer arc dash 虚线的 shader 代码

* style: lint style

* feat: 拆分 lineLayer arc3d linear 部分的 shader 代码

* style: lint style

* style: lint style

* feat: 完善 isMiniAli 的判断,兼容 smallfish H5+ 的模式

* style: lint style

* style: adjust mulpass demo

* feat: 提供 getScale 方法

* style: lint style

* feat:     修复支付宝小程序h5+开发模式下引入l7样式失效问题

* feat: 修改 l7hammerjs 的导入

* fix: 恢复原有的 picking shader 代码,解决移动端高亮存在冲突破面的情况

* feat: 重新设置 l7hammerjs 的导入方式

* fix: 修复 createTexture 的数据类型在 支付宝 环境中使用 Uint8ClampedArray 存在数据类型不兼容的现象

* style: lint style

* feat: 兼容高德地图 2.x 在部分安卓手机上点击拾取失效的情况

* style: lint style

* feat: 优化经典热力图显示效果

* style: lint style

* feat: 修改 setBlend 方法、返回当前的 layer 对象

* feat: 完善在点图层未传入数据的情况下判断 shape 类型

* style: lint style

* feat: 修复 MultiPassRender 在高德1.x 底图下,缩放地图时存在可视化层不同步的现象

* fix: 修复 getModelTypeWillEmptyData 的 bug

* feat: 提供图层空数据的情况下提前指定 layer 类型的参数方法(暂时只在 pointlayer)

* style: lint style

* fix: 修复 l7-component Supercluster 依赖的缺失

* feat: 新增大小不受限制的点图层贴图类型 fillimage

* style: lint style
This commit is contained in:
YiQianYao 2022-03-02 16:08:01 +08:00 committed by GitHub
parent a10d0b6aad
commit d0476b7484
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 474 additions and 66 deletions

View File

@ -343,6 +343,7 @@ export interface ILayerConfig {
enableMultiPassRenderer: boolean; enableMultiPassRenderer: boolean;
passes: Array<string | [string, { [key: string]: unknown }]>; passes: Array<string | [string, { [key: string]: unknown }]>;
// layerType 指定 shape 的类型
layerType?: string | undefined; layerType?: string | undefined;
forward: boolean; // 正方向 forward: boolean; // 正方向

View File

@ -53,6 +53,7 @@ export default class PointLayer extends BaseLayer<IPointLayerStyleOptions> {
protected getDefaultConfig() { protected getDefaultConfig() {
const type = this.getModelType(); const type = this.getModelType();
const defaultConfig = { const defaultConfig = {
fillImage: {},
normal: { normal: {
blend: 'additive', blend: 'additive',
}, },
@ -70,6 +71,7 @@ export default class PointLayer extends BaseLayer<IPointLayerStyleOptions> {
protected getModelType(): PointType { protected getModelType(): PointType {
const PointTypes = [ const PointTypes = [
'fillImage',
'fill', 'fill',
'image', 'image',
'normal', 'normal',
@ -100,6 +102,9 @@ export default class PointLayer extends BaseLayer<IPointLayerStyleOptions> {
if (shape === 'simple') { if (shape === 'simple') {
return 'simplePoint'; return 'simplePoint';
} }
if (shape === 'fillImage') {
return 'fillImage';
}
if (shape2d?.indexOf(shape as string) !== -1) { if (shape2d?.indexOf(shape as string) !== -1) {
return 'fill'; return 'fill';
} }

View File

@ -0,0 +1,323 @@
import {
AttributeType,
gl,
IAttribute,
IElements,
IEncodeFeature,
IModel,
IModelUniform,
ITexture2D,
} from '@antv/l7-core';
import { getMask } from '@antv/l7-utils';
import BaseModel from '../../core/BaseModel';
import { IPointLayerStyleOptions } from '../../core/interface';
import { PointFillTriangulation } from '../../core/triangulation';
// static pointLayer shader - not support animate
import pointFillFrag from '../shaders/image/fillImage_frag.glsl';
import pointFillVert from '../shaders/image/fillImage_vert.glsl';
import { isNumber } from 'lodash';
import { Version } from '@antv/l7-maps';
export default class FillImageModel extends BaseModel {
public meter2coord: number = 1;
private texture: ITexture2D;
private isMeter: boolean = false;
public getUninforms(): IModelUniform {
const {
opacity = 1,
strokeOpacity = 1,
strokeWidth = 0,
stroke = 'rgba(0,0,0,0)',
offsets = [0, 0],
blend,
} = this.layer.getLayerConfig() as IPointLayerStyleOptions;
if (this.rendererService.getDirty()) {
this.texture.bind();
}
if (
this.dataTextureTest &&
this.dataTextureNeedUpdate({
opacity,
strokeOpacity,
strokeWidth,
stroke,
offsets,
})
) {
// 判断当前的样式中哪些是需要进行数据映射的,哪些是常量,同时计算用于构建数据纹理的一些中间变量
this.judgeStyleAttributes({
opacity,
strokeOpacity,
strokeWidth,
stroke,
offsets,
});
const encodeData = this.layer.getEncodedData();
const { data, width, height } = this.calDataFrame(
this.cellLength,
encodeData,
this.cellProperties,
);
this.rowCount = height; // 当前数据纹理有多少行
this.dataTexture =
this.cellLength > 0 && data.length > 0
? this.createTexture2D({
flipY: true,
data,
format: gl.LUMINANCE,
type: gl.FLOAT,
width,
height,
})
: this.createTexture2D({
flipY: true,
data: [1],
format: gl.LUMINANCE,
type: gl.FLOAT,
width: 1,
height: 1,
});
}
return {
u_isMeter: Number(this.isMeter),
u_additive: blend === 'additive' ? 1.0 : 0.0,
u_dataTexture: this.dataTexture, // 数据纹理 - 有数据映射的时候纹理中带数据,若没有任何数据映射时纹理是 [1]
u_cellTypeLayout: this.getCellTypeLayout(),
u_texture: this.texture,
u_textSize: [1024, this.iconService.canvasHeight || 128],
u_opacity: isNumber(opacity) ? opacity : 1.0,
u_offsets: this.isOffsetStatic(offsets)
? (offsets as [number, number])
: [0, 0],
};
}
public getAttribute(): {
attributes: {
[attributeName: string]: IAttribute;
};
elements: IElements;
} {
return this.styleAttributeService.createAttributesAndIndices(
this.layer.getEncodedData(),
PointFillTriangulation,
);
}
public initModels(): IModel[] {
this.updateTexture();
this.iconService.on('imageUpdate', this.updateTexture);
const {
unit = 'l7size',
} = this.layer.getLayerConfig() as IPointLayerStyleOptions;
const { version } = this.mapService;
if (
unit === 'meter' &&
version !== Version.L7MAP &&
version !== Version.GLOBEL
) {
this.isMeter = true;
this.calMeter2Coord();
}
return this.buildModels();
}
/**
* unit meter
* @returns
*/
public calMeter2Coord() {
// @ts-ignore
const [minLng, minLat, maxLng, maxLat] = this.layer.getSource().extent;
const center = [(minLng + maxLng) / 2, (minLat + maxLat) / 2];
const { version } = this.mapService;
if (version === Version.MAPBOX && window.mapboxgl.MercatorCoordinate) {
const coord = window.mapboxgl.MercatorCoordinate.fromLngLat(
{ lng: center[0], lat: center[1] },
0,
);
const offsetInMeters = 1;
const offsetInMercatorCoordinateUnits =
offsetInMeters * coord.meterInMercatorCoordinateUnits();
const westCoord = new window.mapboxgl.MercatorCoordinate(
coord.x - offsetInMercatorCoordinateUnits,
coord.y,
coord.z,
);
const westLnglat = westCoord.toLngLat();
this.meter2coord = center[0] - westLnglat.lng;
return;
}
// @ts-ignore
const m1 = this.mapService.meterToCoord(center, [minLng, minLat]);
// @ts-ignore
const m2 = this.mapService.meterToCoord(center, [
maxLng === minLng ? maxLng + 0.1 : maxLng,
maxLat === minLat ? minLat + 0.1 : maxLat,
]);
this.meter2coord = (m1 + m2) / 2;
if (!Boolean(this.meter2coord)) {
// Tip: 兼容单个数据导致的 m1、m2 为 NaN
this.meter2coord = 7.70681090738883;
}
}
public buildModels(): IModel[] {
const {
mask = false,
maskInside = true,
} = this.layer.getLayerConfig() as IPointLayerStyleOptions;
const { frag, vert, type } = this.getShaders();
return [
this.layer.buildLayerModel({
moduleName: 'pointfill-' + type,
vertexShader: vert,
fragmentShader: frag,
triangulation: PointFillTriangulation,
depth: { enable: false },
blend: this.getBlend(),
stencil: getMask(mask, maskInside),
}),
];
}
public getShaders(): { frag: string; vert: string; type: string } {
return {
frag: pointFillFrag,
vert: pointFillVert,
type: 'normal',
};
}
public clearModels() {
this.dataTexture?.destroy();
}
// overwrite baseModel func
protected registerBuiltinAttributes() {
this.styleAttributeService.registerStyleAttribute({
name: 'uv',
type: AttributeType.Attribute,
descriptor: {
name: 'a_Uv',
buffer: {
// give the WebGL driver a hint that this buffer may change
usage: gl.DYNAMIC_DRAW,
data: [],
type: gl.FLOAT,
},
size: 2,
update: (
feature: IEncodeFeature,
featureIdx: number,
vertex: number[],
attributeIdx: number,
) => {
const iconMap = this.iconService.getIconMap();
const { shape } = feature;
const { x, y } = iconMap[shape as string] || { x: 0, y: 0 };
return [x, y];
},
},
});
this.styleAttributeService.registerStyleAttribute({
name: 'extrude',
type: AttributeType.Attribute,
descriptor: {
name: 'a_Extrude',
buffer: {
// give the WebGL driver a hint that this buffer may change
usage: gl.DYNAMIC_DRAW,
data: [],
type: gl.FLOAT,
},
size: 3,
update: (
feature: IEncodeFeature,
featureIdx: number,
vertex: number[],
attributeIdx: number,
) => {
const extrude = [1, 1, 0, -1, 1, 0, -1, -1, 0, 1, -1, 0];
const extrudeIndex = (attributeIdx % 4) * 3;
return [
extrude[extrudeIndex],
extrude[extrudeIndex + 1],
extrude[extrudeIndex + 2],
];
},
},
});
// point layer size;
this.styleAttributeService.registerStyleAttribute({
name: 'size',
type: AttributeType.Attribute,
descriptor: {
name: 'a_Size',
buffer: {
// give the WebGL driver a hint that this buffer may change
usage: gl.DYNAMIC_DRAW,
data: [],
type: gl.FLOAT,
},
size: 1,
update: (
feature: IEncodeFeature,
featureIdx: number,
vertex: number[],
attributeIdx: number,
) => {
const { size = 5 } = feature;
// console.log('featureIdx', featureIdx, feature)
return Array.isArray(size)
? [size[0] * this.meter2coord]
: [(size as number) * this.meter2coord];
},
},
});
}
private updateTexture = () => {
const { createTexture2D } = this.rendererService;
if (this.texture) {
this.texture.update({
data: this.iconService.getCanvas(),
mag: 'linear',
min: 'linear mipmap nearest',
mipmap: true,
});
// this.layer.render();
// TODO: 更新完纹理后在更新的图层的时候需要更新所有的图层
this.layer.renderLayers();
return;
}
this.texture = createTexture2D({
data: this.iconService.getCanvas(),
mag: gl.LINEAR,
// min: gl.LINEAR,
min: gl.LINEAR_MIPMAP_LINEAR,
premultiplyAlpha: false,
width: 1024,
height: this.iconService.canvasHeight || 128,
mipmap: true,
});
};
}

View File

@ -1,5 +1,6 @@
import ExtrudeModel from './extrude'; import ExtrudeModel from './extrude';
import FillModel from './fill'; import FillModel from './fill';
import FillImageModel from './fillmage';
import IconModel from './icon-font'; import IconModel from './icon-font';
import IMageModel from './image'; import IMageModel from './image';
import NormalModel from './normal'; import NormalModel from './normal';
@ -7,6 +8,7 @@ import SimplePopint from './simplePoint';
import TextModel from './text'; import TextModel from './text';
export type PointType = export type PointType =
| 'fillImage'
| 'fill' | 'fill'
| 'image' | 'image'
| 'normal' | 'normal'
@ -16,6 +18,7 @@ export type PointType =
| 'icon'; | 'icon';
const PointModels: { [key in PointType]: any } = { const PointModels: { [key in PointType]: any } = {
fillImage: FillImageModel,
fill: FillModel, fill: FillModel,
image: IMageModel, image: IMageModel,
normal: NormalModel, normal: NormalModel,

View File

@ -0,0 +1,24 @@
uniform sampler2D u_texture;
uniform vec2 u_textSize;
uniform float u_additive;
varying mat4 styleMappingMat; // 传递从片元中传递的映射数据
varying float v_radius;
#pragma include "sdf_2d"
#pragma include "picking"
varying vec2 v_uv; // 本身的 uv 坐标
varying vec2 v_Iconuv;
void main() {
float opacity = styleMappingMat[0][0];
vec2 pos = v_Iconuv / u_textSize + v_uv / u_textSize * 64.;
gl_FragColor = texture2D(u_texture, pos);
gl_FragColor.a *= opacity;
gl_FragColor = filterColor(gl_FragColor);
}

View File

@ -0,0 +1,110 @@
attribute vec4 a_Color;
attribute vec3 a_Position;
attribute vec3 a_Extrude;
attribute float a_Size;
attribute vec2 a_Uv;
varying mat4 styleMappingMat; // 用于将在顶点着色器中计算好的样式值传递给片元
uniform mat4 u_ModelMatrix;
uniform mat4 u_Mvp;
uniform float u_isMeter;
varying float v_radius;
varying vec2 v_uv; // 本身的 uv 坐标
varying vec2 v_Iconuv; // icon 贴图的 uv 坐标
uniform float u_opacity : 1;
uniform vec2 u_offsets;
#pragma include "styleMapping"
#pragma include "styleMappingCalOpacity"
#pragma include "projection"
#pragma include "picking"
void main() {
vec3 extrude = a_Extrude;
v_uv = (a_Extrude.xy + 1.0)/2.0;
v_uv.y = 1.0 - v_uv.y;
v_Iconuv = a_Uv;
// cal style mapping - 数据纹理映射部分的计算
styleMappingMat = mat4(
0.0, 0.0, 0.0, 0.0, // opacity - empty - empty - empty
0.0, 0.0, 0.0, 0.0, // empty - empty - empty - empty
0.0, 0.0, 0.0, 0.0, // offsets[0] - offsets[1]
0.0, 0.0, 0.0, 0.0
);
float rowCount = u_cellTypeLayout[0][0]; // 当前的数据纹理有几行
float columnCount = u_cellTypeLayout[0][1]; // 当看到数据纹理有几列
float columnWidth = 1.0/columnCount; // 列宽
float rowHeight = 1.0/rowCount; // 行高
float cellCount = calCellCount(); // opacity - strokeOpacity - strokeWidth - stroke - offsets
float id = a_vertexId; // 第n个顶点
float cellCurrentRow = floor(id * cellCount / columnCount) + 1.0; // 起始点在第几行
float cellCurrentColumn = mod(id * cellCount, columnCount) + 1.0; // 起始点在第几列
// cell 固定顺序 opacity -> strokeOpacity -> strokeWidth -> stroke ...
// 按顺序从 cell 中取值、若没有则自动往下取值
float textureOffset = 0.0; // 在 cell 中取值的偏移量
vec2 opacityAndOffset = calOpacityAndOffset(cellCurrentRow, cellCurrentColumn, columnCount, textureOffset, columnWidth, rowHeight);
styleMappingMat[0][0] = opacityAndOffset.r;
textureOffset = opacityAndOffset.g;
vec2 textrueOffsets = vec2(0.0, 0.0);
if(hasOffsets()) {
vec2 valueXPos = nextPos(cellCurrentRow, cellCurrentColumn, columnCount, textureOffset);
textrueOffsets.r = pos2value(valueXPos, columnWidth, rowHeight); // x
textureOffset += 1.0;
vec2 valueYPos = nextPos(cellCurrentRow, cellCurrentColumn, columnCount, textureOffset);
textrueOffsets.g = pos2value(valueYPos, columnWidth, rowHeight); // x
textureOffset += 1.0;
} else {
textrueOffsets = u_offsets;
}
// cal style mapping
// radius(16-bit)
v_radius = a_Size;
// TODO: billboard
// anti-alias
vec2 offset = (extrude.xy * (a_Size) + textrueOffsets);
vec3 aPosition = a_Position;
if(u_isMeter < 1.0) {
// 不以米为实际单位
offset = project_pixel(offset);
} else {
// 以米为实际单位
if(u_CoordinateSystem == COORDINATE_SYSTEM_LNGLAT || u_CoordinateSystem == COORDINATE_SYSTEM_LNGLAT_OFFSET) {
aPosition.xy += offset;
offset.x = 0.0;
offset.y = 0.0;
}
}
// vec4 project_pos = project_position(vec4(a_Position.xy, 0.0, 1.0));
vec4 project_pos = project_position(vec4(aPosition.xy, 0.0, 1.0));
// gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy + offset, project_pixel(setPickingOrder(0.0)), 1.0));
if(u_CoordinateSystem == COORDINATE_SYSTEM_P20_2) { // gaode2.x
gl_Position = u_Mvp * vec4(project_pos.xy + offset, 0.0, 1.0);
} else {
gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy + offset, project_pixel(setPickingOrder(0.0)), 1.0));
}
// gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy + offset, 0.0, 1.0));
setPickingColor(a_PickingColor);
}

View File

@ -129,77 +129,19 @@ export default class Amap2demo extends React.Component {
); );
scene.on('loaded', () => { scene.on('loaded', () => {
// fetch( const imageLayer = new PointLayer({ layerType: 'fillImage' })
// 'https://gw.alipayobjects.com/os/basement_prod/893d1d5f-11d9-45f3-8322-ee9140d288ae.json',
// )
// .then((res) => res.json())
// .then((data) => {
// const imageLayer = new PointLayer()
// .source(data, {
// parser: {
// type: 'json',
// x: 'longitude',
// y: 'latitude',
// },
// })
// .shape('name', ['00'])
// .size(10);
// let d = {
// coordinates: (2)[(121.4318415, 31.25682515)],
// count: 2,
// id: '5011000005647',
// latitude: 31.25682515,
// longitude: 121.4318415,
// name: '石泉路140弄',
// unit_price: 52256.3,
// };
// const imageLayer1 = new PointLayer()
// .source([], {
// parser: {
// type: 'json',
// x: 'longitude',
// y: 'latitude',
// },
// })
// .shape('name', ['00'])
// .size(25);
// scene.addLayer(imageLayer);
// scene.addLayer(imageLayer1);
// imageLayer.on('click', (e) => {
// // console.log(e);
// // imageLayer1.setBlend('normal')
// imageLayer1.setData([e.feature]);
// });
// });
const imageLayer = new PointLayer()
.source(data) .source(data)
// .shape('fillImage', s => s)
.shape('s', (s) => s) .shape('s', (s) => s)
.size(10) // .color('#f00')
.size(100)
.active({
color: '#f00',
mix: 0.5,
})
.fitBounds(); .fitBounds();
const imageLayer1 = new PointLayer({ layerType: 'image' })
.source({
features: [],
type: 'FeatureCollection',
})
.shape('s', (s) => s)
.size(20);
scene.addLayer(imageLayer); scene.addLayer(imageLayer);
scene.addLayer(imageLayer1);
imageLayer.on('click', (e) => {
console.log(e.feature);
imageLayer1.setData({
features: [e.feature],
type: 'FeatureCollection',
});
});
}); });
} }