mirror of https://gitee.com/antv-l7/antv-l7
Shihui (#997)
* feat: add water layer * feat: 新增 waterLayer * style: lint style
This commit is contained in:
parent
bd91e9f920
commit
50cc41da17
|
@ -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;
|
||||
|
|
|
@ -91,6 +91,10 @@ export interface IPolygonLayerStyleOptions {
|
|||
pickLight: boolean;
|
||||
mask?: boolean;
|
||||
maskInside?: boolean;
|
||||
|
||||
// water
|
||||
waterTexture?: string;
|
||||
speed?: number;
|
||||
}
|
||||
|
||||
export interface IImageLayerStyleOptions {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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 recommend:https://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();
|
||||
};
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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 />)
|
Loading…
Reference in New Issue