* feat: add water layer

* feat: 新增 waterLayer

* style: lint style
This commit is contained in:
YiQianYao 2022-03-09 17:48:23 +08:00 committed by GitHub
parent bd91e9f920
commit 50cc41da17
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 395 additions and 1 deletions

View File

@ -1,3 +1,4 @@
import { BBox } from '@turf/helpers';
export type DataType = string | object[] | object;
export interface IParserCfg {
type: string;
@ -61,6 +62,7 @@ export interface ISource {
data: IParserData;
cluster: boolean;
clusterOptions: Partial<IClusterOptions>;
extent: BBox;
setData(data: any): void;
updateClusterData(zoom: number): void;
getFeatureById(id: number): unknown;

View File

@ -91,6 +91,10 @@ export interface IPolygonLayerStyleOptions {
pickLight: boolean;
mask?: boolean;
maskInside?: boolean;
// water
waterTexture?: string;
speed?: number;
}
export interface IImageLayerStyleOptions {

View File

@ -34,6 +34,8 @@ export default class PolygonLayer extends BaseLayer<IPolygonLayerStyleOptions> {
return 'fill';
} else if (shape === 'extrude') {
return 'extrude';
} else if (shape === 'water') {
return 'water';
} else if (shape === 'line') {
return 'line';
} else {

View File

@ -6,6 +6,7 @@ import NormalModel from '../../point/models/normal';
import TextModel from '../../point/models/text';
import ExtrudeModel from './extrude';
import FillModel from './fill';
import Water from './water';
export type PolygonModelType =
| 'fill'
@ -15,7 +16,8 @@ export type PolygonModelType =
| 'point_image'
| 'point_normal'
| 'point_extrude'
| 'text';
| 'text'
| 'water';
const PolygonModels: { [key in PolygonModelType]: any } = {
fill: FillModel,
@ -26,6 +28,7 @@ const PolygonModels: { [key in PolygonModelType]: any } = {
point_image: IMageModel,
point_normal: NormalModel,
point_extrude: PointExtrudeModel,
water: Water,
// point_fill: PointModels.fill,
};

View File

@ -0,0 +1,165 @@
import {
AttributeType,
gl,
IEncodeFeature,
IModel,
IModelUniform,
ITexture2D,
} from '@antv/l7-core';
import { getMask } from '@antv/l7-utils';
import { isNumber } from 'lodash';
import BaseModel from '../../core/BaseModel';
import { IPolygonLayerStyleOptions } from '../../core/interface';
import { polygonTriangulation } from '../../core/triangulation';
import water_frag from '../shaders/water/polygon_water_frag.glsl';
import water_vert from '../shaders/water/polygon_water_vert.glsl';
export default class WaterModel extends BaseModel {
private texture: ITexture2D;
public getUninforms() {
const {
opacity = 1,
speed = 0.5,
} = this.layer.getLayerConfig() as IPolygonLayerStyleOptions;
if (this.dataTextureTest && this.dataTextureNeedUpdate({ opacity })) {
this.judgeStyleAttributes({ opacity });
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_texture: this.texture,
u_dataTexture: this.dataTexture, // 数据纹理 - 有数据映射的时候纹理中带数据,若没有任何数据映射时纹理是 [1]
u_cellTypeLayout: this.getCellTypeLayout(),
u_speed: speed,
u_opacity: isNumber(opacity) ? opacity : 1.0,
};
}
public getAnimateUniforms(): IModelUniform {
return {
u_time: this.layer.getLayerAnimateTime(),
};
}
public initModels(): IModel[] {
this.loadTexture();
return this.buildModels();
}
public buildModels(): IModel[] {
const {
mask = false,
maskInside = true,
} = this.layer.getLayerConfig() as IPolygonLayerStyleOptions;
return [
this.layer.buildLayerModel({
moduleName: 'polygon_water',
vertexShader: water_vert,
fragmentShader: water_frag,
triangulation: polygonTriangulation,
depth: { enable: false },
stencil: getMask(mask, maskInside),
}),
];
}
public clearModels() {
this.texture.destroy();
this.dataTexture?.destroy();
}
protected registerBuiltinAttributes() {
const bbox = this.layer.getSource().extent;
const [minLng, minLat, maxLng, maxLat] = bbox;
const lngLen = maxLng - minLng;
const latLen = maxLat - minLat;
this.styleAttributeService.registerStyleAttribute({
name: 'linear',
type: AttributeType.Attribute,
descriptor: {
name: 'a_uv',
buffer: {
// give the WebGL driver a hint that this buffer may change
usage: gl.STATIC_DRAW,
data: [],
type: gl.FLOAT,
},
size: 2,
update: (
feature: IEncodeFeature,
featureIdx: number,
vertex: number[],
attributeIdx: number,
normal: number[],
) => {
const [lng, lat] = vertex;
return [(lng - minLng) / lngLen, (lat - minLat) / latLen];
},
},
});
}
private loadTexture() {
const {
waterTexture,
} = this.layer.getLayerConfig() as IPolygonLayerStyleOptions;
const { createTexture2D } = this.rendererService;
this.texture = createTexture2D({
height: 0,
width: 0,
});
const image = new Image();
image.crossOrigin = '';
if (waterTexture) {
// custom texture
console.warn(
'L7 recommendhttps://gw.alipayobjects.com/mdn/rms_816329/afts/img/A*EojwT4VzSiYAAAAAAAAAAAAAARQnAQ',
);
image.src = waterTexture;
} else {
// default texture
image.src =
'https://gw.alipayobjects.com/mdn/rms_816329/afts/img/A*EojwT4VzSiYAAAAAAAAAAAAAARQnAQ';
}
image.onload = () => {
this.texture = createTexture2D({
data: image,
width: image.width,
height: image.height,
wrapS: gl.MIRRORED_REPEAT,
wrapT: gl.MIRRORED_REPEAT,
min: gl.LINEAR,
mag: gl.LINEAR,
});
this.layerService.updateLayerRenderList();
this.layerService.renderLayers();
};
}
}

View File

@ -0,0 +1,75 @@
uniform sampler2D u_texture;
uniform float u_time;
uniform float u_speed: 1.0;
uniform float u_opacity: 1.0;
varying vec4 v_Color;
varying vec2 v_uv;
varying mat4 styleMappingMat; // 传递从片元中传递的映射数据
#pragma include "picking"
float rand(vec2 n) { return 0.5 + 0.5 * fract(sin(dot(n.xy, vec2(12.9898, 78.233)))* 43758.5453); }
float water(vec3 p) {
float t = u_time * u_speed;
p.z += t * 2.; p.x += t * 2.;
vec3 c1 = texture2D(u_texture, p.xz / 30.).xyz;
p.z += t * 3.; p.x += t * 0.52;
vec3 c2 = texture2D(u_texture, p.xz / 30.).xyz;
p.z += t * 4.; p.x += t * 0.8;
vec3 c3 = texture2D(u_texture, p.xz / 30.).xyz;
c1 += c2 - c3;
float z = (c1.x + c1.y + c1.z) / 3.;
return p.y + z / 4.;
}
float map(vec3 p) {
float d = 100.0;
d = water(p);
return d;
}
float intersect(vec3 ro, vec3 rd) {
float d = 0.0;
for (int i = 0; i <= 100; i++) {
float h = map(ro + rd * d);
if (h < 0.1) return d;
d += h;
}
return 0.0;
}
vec3 norm(vec3 p) {
float eps = .1;
return normalize(vec3(
map(p + vec3(eps, 0, 0)) - map(p + vec3(-eps, 0, 0)),
map(p + vec3(0, eps, 0)) - map(p + vec3(0, -eps, 0)),
map(p + vec3(0, 0, eps)) - map(p + vec3(0, 0, -eps))
));
}
float calSpc() {
vec3 l1 = normalize(vec3(1, 1, 1));
vec3 ro = vec3(-3, 20, -8);
vec3 rc = vec3(0, 0, 0);
vec3 ww = normalize(rc - ro);
vec3 uu = normalize(cross(vec3(0,1,0), ww));
vec3 vv = normalize(cross(rc - ro, uu));
vec3 rd = normalize(uu * v_uv.x + vv * v_uv.y + ww);
float d = intersect(ro, rd);
vec3 p = ro + rd * d;
vec3 n = norm(p);
float spc = pow(max(0.0, dot(reflect(l1, n), rd)), 30.0);
return spc;
}
void main() {
float opacity = styleMappingMat[0][0];
gl_FragColor = v_Color;
gl_FragColor.a *= opacity;
float spc = calSpc();
gl_FragColor += spc * 0.4;
}

View File

@ -0,0 +1,58 @@
attribute vec4 a_Color;
attribute vec2 a_uv;
attribute vec3 a_Position;
uniform mat4 u_ModelMatrix;
uniform mat4 u_Mvp;
varying vec4 v_Color;
varying vec2 v_uv;
uniform float u_opacity: 1.0;
varying mat4 styleMappingMat; // 用于将在顶点着色器中计算好的样式值传递给片元
#pragma include "styleMapping"
#pragma include "styleMappingCalOpacity"
#pragma include "projection"
#pragma include "picking"
void main() {
v_uv = a_uv;
// cal style mapping - 数据纹理映射部分的计算
styleMappingMat = mat4(
0.0, 0.0, 0.0, 0.0, // opacity - strokeOpacity - strokeWidth - empty
0.0, 0.0, 0.0, 0.0, // strokeR - strokeG - strokeB - strokeA
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;
// cal style mapping - 数据纹理映射部分的计算
v_Color = a_Color;
vec4 project_pos = project_position(vec4(a_Position, 1.0));
// gl_Position = project_common_position_to_clipspace(vec4(project_pos.xyz, 1.0));
if(u_CoordinateSystem == COORDINATE_SYSTEM_P20_2) { // gaode2.x
gl_Position = u_Mvp * (vec4(project_pos.xyz, 1.0));
} else {
gl_Position = project_common_position_to_clipspace(vec4(project_pos.xyz, 1.0));
}
setPickingColor(a_PickingColor);
}

View File

@ -0,0 +1,79 @@
import { PolygonLayer, Scene } from '@antv/l7';
import { GaodeMap } from '@antv/l7-maps';
import * as React from 'react';
export default class Amap2demo_polygon extends React.Component {
private scene: Scene;
public componentWillUnmount() {
this.scene.destroy();
}
public async componentDidMount() {
const scene = new Scene({
id: 'map',
map: new GaodeMap({
pitch: 0,
center: [-44.40673828125, -18.375379094031825],
zoom: 13,
}),
});
this.scene = scene;
const data = {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
properties: {
testOpacity: 0.4,
},
geometry: {
type: 'MultiPolygon',
coordinates: [
[
[
[111.26953125, 33.52307880890422],
[111.26953125, 34.03445260967645],
[112.03857421875, 34.03445260967645],
[112.03857421875, 33.52307880890422],
[111.26953125, 33.52307880890422],
],
],
],
},
},
],
};
fetch(
'https://gw.alipayobjects.com/os/bmw-prod/67130c6c-7f49-4680-915c-54e69730861d.json',
)
.then((data) => data.json())
.then(({ lakeData }) => {
const lakeLayer = new PolygonLayer({ autoFit: true })
.source(lakeData)
.shape('water')
.color('#1E90FF')
.style({
speed: 0.4,
// waterTexture: 'https://gw.alipayobjects.com/mdn/rms_816329/afts/img/A*EojwT4VzSiYAAAAAAAAAAAAAARQnAQ'
})
.animate(true);
scene.addLayer(lakeLayer);
});
}
public render() {
return (
<div
id="map"
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
}}
/>
);
}
}

View File

@ -0,0 +1,6 @@
import { storiesOf } from '@storybook/react';
import * as React from 'react';
import Water from './components/water';
storiesOf('Object', module)
.add('water', () => <Water />)