diff --git a/packages/layers/src/core/interface.ts b/packages/layers/src/core/interface.ts index cc19a5b10b..f93cc0e2d4 100644 --- a/packages/layers/src/core/interface.ts +++ b/packages/layers/src/core/interface.ts @@ -95,6 +95,9 @@ export interface IPolygonLayerStyleOptions { // water waterTexture?: string; speed?: number; + // ocean + watercolor?: string; + watercolor2?: string; } export interface IImageLayerStyleOptions { diff --git a/packages/layers/src/polygon/index.ts b/packages/layers/src/polygon/index.ts index db996a6133..84de7cd93a 100644 --- a/packages/layers/src/polygon/index.ts +++ b/packages/layers/src/polygon/index.ts @@ -36,6 +36,8 @@ export default class PolygonLayer extends BaseLayer { return 'extrude'; } else if (shape === 'water') { return 'water'; + } else if (shape === 'ocean') { + return 'ocean'; } else if (shape === 'line') { return 'line'; } else { diff --git a/packages/layers/src/polygon/models/index.ts b/packages/layers/src/polygon/models/index.ts index 811d5e1762..74c2accb0a 100644 --- a/packages/layers/src/polygon/models/index.ts +++ b/packages/layers/src/polygon/models/index.ts @@ -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 Ocean from './ocean'; import Water from './water'; export type PolygonModelType = @@ -17,7 +18,8 @@ export type PolygonModelType = | 'point_normal' | 'point_extrude' | 'text' - | 'water'; + | 'water' + | 'ocean'; const PolygonModels: { [key in PolygonModelType]: any } = { fill: FillModel, @@ -29,7 +31,7 @@ const PolygonModels: { [key in PolygonModelType]: any } = { point_normal: NormalModel, point_extrude: PointExtrudeModel, water: Water, - + ocean: Ocean, // point_fill: PointModels.fill, }; export default PolygonModels; diff --git a/packages/layers/src/polygon/models/ocean.ts b/packages/layers/src/polygon/models/ocean.ts new file mode 100644 index 0000000000..cd9adb3493 --- /dev/null +++ b/packages/layers/src/polygon/models/ocean.ts @@ -0,0 +1,186 @@ +import { + AttributeType, + gl, + IEncodeFeature, + IModel, + IModelUniform, + ITexture2D, +} from '@antv/l7-core'; +import { getMask, rgb2arr } from '@antv/l7-utils'; +import { create, isNumber } from 'lodash'; +import BaseModel from '../../core/BaseModel'; +import { IPolygonLayerStyleOptions } from '../../core/interface'; +import { polygonTriangulation } from '../../core/triangulation'; +import ocean_frag from '../shaders/water/polygon_ocean_frag.glsl'; +import ocean_vert from '../shaders/water/polygon_ocean_vert.glsl'; +export default class OceanModel extends BaseModel { + private texture1: ITexture2D; + private texture2: ITexture2D; + private texture3: ITexture2D; + public getUninforms() { + const { + opacity = 1, + watercolor = '#6D99A8', + watercolor2 = '#0F121C', + } = 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_texture1: this.texture1, + u_texture2: this.texture2, + u_texture3: this.texture3, + u_watercolor: rgb2arr(watercolor), + u_watercolor2: rgb2arr(watercolor2), + u_dataTexture: this.dataTexture, // 数据纹理 - 有数据映射的时候纹理中带数据,若没有任何数据映射时纹理是 [1] + u_cellTypeLayout: this.getCellTypeLayout(), + 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_ocean', + vertexShader: ocean_vert, + fragmentShader: ocean_frag, + triangulation: polygonTriangulation, + depth: { enable: false }, + + stencil: getMask(mask, maskInside), + }), + ]; + } + + public clearModels() { + this.texture1.destroy(); + this.texture2.destroy(); + this.texture3.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 { createTexture2D } = this.rendererService; + const defaultTextureOptions = { height: 0, width: 0 }; + // 默认索引为 undefined,所以单独赋值 + this.texture1 = createTexture2D(defaultTextureOptions); + this.texture2 = createTexture2D(defaultTextureOptions); + this.texture3 = createTexture2D(defaultTextureOptions); + + // 加载完 image 后单独给 texture f赋值 + initImage((images: HTMLImageElement[]) => { + this.texture1 = initTex(images[0]); + this.texture2 = initTex(images[1]); + this.texture3 = initTex(images[2]); + this.layerService.updateLayerRenderList(); + this.layerService.renderLayers(); + }); + + function initImage(callback: (loadedImages: HTMLImageElement[]) => void) { + let loadedCount = 0; + const loadedImages: HTMLImageElement[] = []; + const images = [ + 'https://gw.alipayobjects.com/mdn/rms_816329/afts/img/A*EojwT4VzSiYAAAAAAAAAAAAAARQnAQ', + 'https://gw.alipayobjects.com/mdn/rms_816329/afts/img/A*MJ22QbpuCzIAAAAAAAAAAAAAARQnAQ', + 'https://gw.alipayobjects.com/mdn/rms_816329/afts/img/A*-z2HSIVDsHIAAAAAAAAAAAAAARQnAQ', + ]; + images.map((imgSrc: string) => { + const image = new Image(); + image.crossOrigin = ''; + image.src = imgSrc; + loadedImages.push(image); + image.onload = () => { + loadedCount++; + if (loadedCount === 3) { + callback(loadedImages); + } + }; + }); + } + + function initTex(image: HTMLImageElement) { + return createTexture2D({ + data: image, + width: image.width, + height: image.height, + wrapS: gl.MIRRORED_REPEAT, + wrapT: gl.MIRRORED_REPEAT, + min: gl.LINEAR, + mag: gl.LINEAR, + }); + } + } +} diff --git a/packages/layers/src/polygon/shaders/water/polygon_ocean_frag.glsl b/packages/layers/src/polygon/shaders/water/polygon_ocean_frag.glsl new file mode 100644 index 0000000000..ed3f313f32 --- /dev/null +++ b/packages/layers/src/polygon/shaders/water/polygon_ocean_frag.glsl @@ -0,0 +1,254 @@ + +uniform float u_time; +uniform float u_opacity: 1.0; + +varying vec4 v_Color; +varying vec2 v_uv; +varying mat4 styleMappingMat; // 传递从片元中传递的映射数据 + +#pragma include "picking" + +float coast2water_fadedepth = 0.10; +float large_waveheight = .750; // change to adjust the "heavy" waves +float large_wavesize = 3.4; // factor to adjust the large wave size +float small_waveheight = 0.6; // change to adjust the small random waves +float small_wavesize = 0.5; // factor to ajust the small wave size +float water_softlight_fact = 15.; // range [1..200] (should be << smaller than glossy-fact) +float water_glossylight_fact= 120.; // range [1..200] +float particle_amount = 70.; +// vec3 watercolor = vec3(0.43, 0.60, 0.66); // 'transparent' low-water color (RGB) +// vec3 watercolor2 = vec3(0.06, 0.07, 0.11); // deep-water color (RGB, should be darker than the low-water color) +uniform vec4 u_watercolor; +uniform vec4 u_watercolor2; +vec3 water_specularcolor = vec3(1.3, 1.3, 0.9); // specular Color (RGB) of the water-highlights +#define light vec3(-0., sin(u_time*0.5)*.5 + .35, 2.8) // position of the sun + +uniform sampler2D u_texture1; +uniform sampler2D u_texture2; +uniform sampler2D u_texture3; + + + +float hash( float n ) { + return fract(sin(n)*43758.5453123); +} + +// 2d noise function +float noise1( in vec2 x ) { + vec2 p = floor(x); + vec2 f = smoothstep(0.0, 1.0, fract(x)); + float n = p.x + p.y*57.0; + return mix(mix( hash(n+ 0.0), hash(n+ 1.0),f.x), + mix( hash(n+ 57.0), hash(n+ 58.0),f.x),f.y); +} + +float noise(vec2 p) { + return texture2D(u_texture2,p*vec2(1./256.)).x; +} + +vec4 highness(vec2 p) { + vec4 t = texture2D(u_texture1,fract(p)); + float clipped = -2.0-smoothstep(3.,10.,t.a)*6.9-smoothstep(10.,100.,t.a)*89.9-smoothstep(0.,10000.,t.a)*10000.0; + return clamp(t, 0.0,3.0)+clamp(t/3.0-1.0, 0.0,1.0)+clamp(t/16.0-1.0, 0.0,1.0); +} + +float height_map( vec2 p ) { + vec4 height=highness(p); + /* + height = -0.5+ + 0.5*smoothstep(-100.,0.,-height)+ + 2.75*smoothstep(0.,2.,height)+ + 1.75*smoothstep(2.,4.,height)+ + 2.75*smoothstep(4.,16.,height)+ + 1.5*smoothstep(16.,1000.,height); + */ + + mat2 m = mat2( 0.9563*1.4, -0.2924*1.4, 0.2924*1.4, 0.9563*1.4 ); + //p = p*6.; + float f = 0.6000*noise1( p ); p = m*p*1.1*6.; + f += 0.2500*noise( p ); p = m*p*1.32; + f += 0.1666*noise( p ); p = m*p*1.11; + f += 0.0834*noise( p ); p = m*p*1.12; + f += 0.0634*noise( p ); p = m*p*1.13; + f += 0.0444*noise( p ); p = m*p*1.14; + f += 0.0274*noise( p ); p = m*p*1.15; + f += 0.0134*noise( p ); p = m*p*1.16; + f += 0.0104*noise( p ); p = m*p*1.17; + f += 0.0084*noise( p ); + f = .25*f+dot(height,vec4(-.03125,-.125,.25,.25))*.5; + const float FLAT_LEVEL = 0.92525; + //f = f*0.25+height*0.75; + if (f level) + { + col = CalcTerrain(uv, height); + } + if (height <= level) + { + vec2 dif = vec2(.0, .01); + vec2 pos = uv*15. + vec2(u_time*.01); + float h1 = water_map(pos-dif,waveheight); + float h2 = water_map(pos+dif,waveheight); + float h3 = water_map(pos-dif.yx,waveheight); + float h4 = water_map(pos+dif.yx,waveheight); + vec3 normwater = normalize(vec3(h3-h4, h1-h2, .125)); // norm-vector of the 'bumpy' water-plane + uv += normwater.xy*.002*(level-height); + + col = CalcTerrain(uv, height); + + float coastfade = clamp((level-height)/coast2water_fadedepth, 0., 1.); + float coastfade2= clamp((level-height)/deepwater_fadedepth, 0., 1.); + float intensity = col.r*.2126+col.g*.7152+col.b*.0722; + watercolor = mix(watercolor*intensity, watercolor2, smoothstep(0., 1., coastfade2)); + + vec3 r0 = vec3(uv, WATER_LEVEL); + vec3 rd = normalize( light - r0 ); // ray-direction to the light from water-position + float grad = dot(normwater, rd); // dot-product of norm-vector and light-direction + float specular = pow(grad, water_softlight_fact); // used for soft highlights + float specular2= pow(grad, water_glossylight_fact); // used for glossy highlights + float gradpos = dot(vec3(0., 0., 1.), rd); + float specular1= smoothstep(0., 1., pow(gradpos, 5.)); // used for diffusity (some darker corona around light's specular reflections...) + float watershade = test_shadow( uv, level ); + watercolor *= 2.2+watershade; + watercolor += (.2+.8*watershade) * ((grad-1.0)*.5+specular) * .25; + watercolor /= (1.+specular1*1.25); + watercolor += watershade*specular2*water_specularcolor; + watercolor += watershade*coastfade*(1.-coastfade2)*(vec3(.5, .6, .7)*nautic(uv)+vec3(1., 1., 1.)*particles(uv)); + + col = mix(col, watercolor, coastfade); + } + + + float opacity = styleMappingMat[0][0]; + gl_FragColor = vec4(col, opacity); + + // gl_FragColor = v_Color; + +} diff --git a/packages/layers/src/polygon/shaders/water/polygon_ocean_vert.glsl b/packages/layers/src/polygon/shaders/water/polygon_ocean_vert.glsl new file mode 100644 index 0000000000..c661d573c5 --- /dev/null +++ b/packages/layers/src/polygon/shaders/water/polygon_ocean_vert.glsl @@ -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); +} + diff --git a/stories/Object/components/ocean.tsx b/stories/Object/components/ocean.tsx new file mode 100644 index 0000000000..a149f34d6c --- /dev/null +++ b/stories/Object/components/ocean.tsx @@ -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('ocean') + .color('#1E90FF') + .style({ + watercolor: '#6D99A8', + // watercolor: '#0f0', + }) + .animate(true); + + scene.addLayer(lakeLayer); + }); + } + + public render() { + return ( +
+ ); + } +} diff --git a/stories/Object/map.stories.tsx b/stories/Object/map.stories.tsx index 759b23363e..3fc839810c 100644 --- a/stories/Object/map.stories.tsx +++ b/stories/Object/map.stories.tsx @@ -1,10 +1,12 @@ import { storiesOf } from '@storybook/react'; import * as React from 'react'; import Water from './components/water'; +import Ocean from './components/ocean'; import Taifong from './components/taifeng' import Radar from './components/radar'; storiesOf('Object', module) .add('water', () => ) + .add('Ocean', () => ) .add('Taifong', () => ) .add('Radar', () => ) \ No newline at end of file