diff --git a/.changeset/cyan-badgers-argue.md b/.changeset/cyan-badgers-argue.md new file mode 100644 index 0000000000..fab83c3f3e --- /dev/null +++ b/.changeset/cyan-badgers-argue.md @@ -0,0 +1,5 @@ +--- +'@antv/l7-layers': patch +--- + +fix: 解决 GeometryLayer 在不同底图上的渲染不一致情况 diff --git a/examples/demos/geometry/index.ts b/examples/demos/geometry/index.ts new file mode 100644 index 0000000000..fc2c12d597 --- /dev/null +++ b/examples/demos/geometry/index.ts @@ -0,0 +1,4 @@ +export { plane } from './plane'; +export { rain } from './rain'; +export { snow } from './snow'; +export { terrain } from './terrain'; diff --git a/examples/demos/geometry/plane.ts b/examples/demos/geometry/plane.ts new file mode 100644 index 0000000000..f2455be5b6 --- /dev/null +++ b/examples/demos/geometry/plane.ts @@ -0,0 +1,28 @@ +import { GeometryLayer } from '@antv/l7'; +import type { TestCase } from '../../types'; +import { CaseScene } from '../../utils'; + +export const plane: TestCase = async (options) => { + const scene = await CaseScene({ + ...options, + mapConfig: { + center: [120.1025, 30.2594], + style: 'dark', + zoom: 10, + }, + }); + + const layer = new GeometryLayer() + .shape('plane') + .style({ + opacity: 0.8, + width: 0.074, + height: 0.061, + center: [120.1025, 30.2594], + }) + .active(true) + .color('#ff0'); + scene.addLayer(layer); + + return scene; +}; diff --git a/examples/demos/geometry/rain.ts b/examples/demos/geometry/rain.ts new file mode 100644 index 0000000000..5e8faae4bc --- /dev/null +++ b/examples/demos/geometry/rain.ts @@ -0,0 +1,33 @@ +import { GeometryLayer } from '@antv/l7'; +import type { TestCase } from '../../types'; +import { CaseScene } from '../../utils'; + +export const rain: TestCase = async (options) => { + const scene = await CaseScene({ + ...options, + mapConfig: { + pitch: 90, + style: 'dark', + center: [120, 30], + zoom: 6, + }, + }); + + const layer = new GeometryLayer() + .shape('sprite') + .size(10) + .style({ + opacity: 0.3, + mapTexture: + 'https://gw.alipayobjects.com/mdn/rms_816329/afts/img/A*w2SFSZJp4nIAAAAAAAAAAAAAARQnAQ', // rain + center: [120, 30], + spriteCount: 120, + spriteRadius: 10, + spriteTop: 300, + spriteUpdate: 10, + spriteScale: 0.8, + }); + scene.addLayer(layer); + + return scene; +}; diff --git a/examples/demos/geometry/snow.ts b/examples/demos/geometry/snow.ts new file mode 100644 index 0000000000..8d7a59d706 --- /dev/null +++ b/examples/demos/geometry/snow.ts @@ -0,0 +1,31 @@ +import { GeometryLayer } from '@antv/l7'; +import type { TestCase } from '../../types'; +import { CaseScene } from '../../utils'; + +export const snow: TestCase = async (options) => { + const scene = await CaseScene({ + ...options, + mapConfig: { + pitch: 40, + style: 'dark', + center: [120, 30], + zoom: 6, + }, + }); + + const layer = new GeometryLayer() + .shape('sprite') + .size(10) + .style({ + mapTexture: + 'https://gw.alipayobjects.com/mdn/rms_816329/afts/img/A*zLQwQKBSagYAAAAAAAAAAAAAARQnAQ', // snow + center: [120, 30], + spriteCount: 60, + spriteRadius: 10, + spriteTop: 300, + spriteUpdate: 5, + }); + scene.addLayer(layer); + + return scene; +}; diff --git a/examples/demos/geometry/terrain.ts b/examples/demos/geometry/terrain.ts new file mode 100644 index 0000000000..60f3f3cc8f --- /dev/null +++ b/examples/demos/geometry/terrain.ts @@ -0,0 +1,84 @@ +import { GeometryLayer } from '@antv/l7'; +import type { TestCase } from '../../types'; +import { CaseScene } from '../../utils'; + +export const terrain: TestCase = async (options) => { + const scene = await CaseScene({ + ...options, + mapConfig: { + center: [120.1025, 30.2594], + style: 'dark', + pitch: 65, + rotation: 180, + zoom: 14, + }, + }); + + let currentZoom = 14, + currentModelData = '100x100'; + + const layer = new GeometryLayer().shape('plane').style({ + width: 0.074, + height: 0.061, + center: [120.1025, 30.2594], + widthSegments: 100, + heightSegments: 100, + terrainClipHeight: 1, + mapTexture: + 'https://gw.alipayobjects.com/mdn/rms_23a451/afts/img/A*gA0NRbuOF5cAAAAAAAAAAAAAARQnAQ', + terrainTexture: + 'https://gw.alipayobjects.com/mdn/rms_23a451/afts/img/A*eYFaRYlnnOUAAAAAAAAAAAAAARQnAQ', + rgb2height: (r, g, b) => { + let h = (r * 255.0 * 256.0 * 256.0 + g * 255.0 * 256.0 + b * 255.0) * 0.1; + h = h / 200 - 12750; + h = Math.max(0, h); + return h; + }, + }); + + let modelData10: any, modelData20: any, modelData100: any; + + layer.on('terrainImageLoaded', () => { + modelData10 = layer.layerModel.createModelData({ + widthSegments: 10, + heightSegments: 10, + }); + + modelData20 = layer.layerModel.createModelData({ + widthSegments: 20, + heightSegments: 20, + }); + + modelData100 = layer.layerModel.createModelData({ + widthSegments: 100, + heightSegments: 100, + }); + }); + + scene.on('zoom', () => { + const zoom = Math.floor(scene.getZoom()); + if (currentZoom !== zoom) { + if (zoom > 13) { + if (currentModelData !== '100x100') { + layer.updateModelData(modelData100); + currentModelData = '100x100'; + } + } else if (zoom > 12) { + if (currentModelData !== '20x20') { + layer.updateModelData(modelData20); + currentModelData = '20x20'; + } + } else { + if (currentModelData !== '10x10') { + layer.updateModelData(modelData10); + currentModelData = '10x10'; + } + } + currentZoom = zoom; + } + }); + + scene.addLayer(layer); + + return scene; +}; diff --git a/examples/demos/index.ts b/examples/demos/index.ts index 7615b566f6..7dbbe74b08 100644 --- a/examples/demos/index.ts +++ b/examples/demos/index.ts @@ -5,6 +5,7 @@ import * as CanvasTestCases from './canvas'; import * as ComponentsTestCases from './components'; import * as ExtendTestCases from './extend'; import * as GalleryTestCases from './gallery'; +import * as GeometryTestCases from './geometry'; import * as HeatmapTestCases from './heatmap'; import * as LineTestCases from './line'; import * as MaskTestCases from './mask'; @@ -26,20 +27,24 @@ export const TestCases = new Map([ ['canvas', Object.entries(CanvasTestCases)], ['basemap', Object.entries(BasemapTestCases)], ['components', Object.entries(ComponentsTestCases)], + ['geometry', Object.entries(GeometryTestCases)], ['gallery', Object.entries(GalleryTestCases)], ['webgpu', Object.entries(WebgpuTestCases)], ['extend', Object.entries(ExtendTestCases)], ]); -export function geSnapshotTestsFromNamespace(namespace: string) { - const testcases = TestCases.get(namespace); - if (!testcases) return []; - const demo = testcases - .filter(([, demo]) => Boolean(demo.snapshot)) - .map(([name, demo]) => ({ - name, - snapshot: Boolean(demo.snapshot), - sleepTime: typeof demo?.snapshot === 'object' ? demo.snapshot?.sleepTime : undefined, - })); - return demo; -} +/** + * ge SnapshotTests from namespace + */ +// export function geSnapshotTestsFromNamespace(namespace: string) { +// const testcases = TestCases.get(namespace); +// if (!testcases) return []; +// const demo = testcases +// .filter(([, demo]) => Boolean(demo.snapshot)) +// .map(([name, demo]) => ({ +// name, +// snapshot: Boolean(demo.snapshot), +// sleepTime: typeof demo?.snapshot === 'object' ? demo.snapshot?.sleepTime : undefined, +// })); +// return demo; +// } diff --git a/packages/layers/src/geometry/models/plane.ts b/packages/layers/src/geometry/models/plane.ts index 006d926b30..86d60108f7 100644 --- a/packages/layers/src/geometry/models/plane.ts +++ b/packages/layers/src/geometry/models/plane.ts @@ -1,12 +1,5 @@ -import type { - IAttributeAndElements, - IEncodeFeature, - IModel, - IModelUniform, - ITexture2D, -} from '@antv/l7-core'; +import type { IEncodeFeature, IModel, IModelUniform, ITexture2D } from '@antv/l7-core'; import { AttributeType, gl } from '@antv/l7-core'; -// import { mat4, vec3 } from 'gl-matrix'; import BaseModel from '../../core/BaseModel'; import type { IGeometryLayerStyleOptions } from '../../core/interface'; import planeFrag from '../shaders/plane_frag.glsl'; @@ -83,6 +76,7 @@ export default class PlaneModel extends BaseModel { heightSegments = 1, center = [120, 30], terrainTexture, + rgb2height = (r: number, g: number, b: number) => r + g + b, } = this.layer.getLayerConfig() as IGeometryLayerStyleOptions; const { indices, positions } = this.initPlane( width, @@ -93,7 +87,14 @@ export default class PlaneModel extends BaseModel { ); if (terrainTexture) { // 存在地形贴图的时候会根据地形贴图对顶点进行偏移 - this.loadTerrainTexture(positions, indices); + return this.translateVertex( + positions, + indices, + this.terrainImage, + widthSegments, + heightSegments, + rgb2height, + ); } return { @@ -147,7 +148,8 @@ export default class PlaneModel extends BaseModel { } public async initModels(): Promise { - const { mapTexture } = this.layer.getLayerConfig() as IGeometryLayerStyleOptions; + const { mapTexture, terrainTexture } = + this.layer.getLayerConfig() as IGeometryLayerStyleOptions; this.mapTexture = mapTexture; const { createTexture2D } = this.rendererService; @@ -159,6 +161,10 @@ export default class PlaneModel extends BaseModel { this.updateTexture(mapTexture); this.initUniformsBuffer(); + if (terrainTexture) { + this.terrainImage = await this.loadTerrainImage(terrainTexture); + } + const model = await this.layer.buildLayerModel({ moduleName: 'geometryPlane', vertexShader: planeVert, @@ -282,70 +288,38 @@ export default class PlaneModel extends BaseModel { } } - const oldFeatures = this.layer.getEncodedData(); - const modelData = this.styleAttributeService.createAttributesAndIndices(oldFeatures, () => { - return { - vertices: positions, - indices, - size: 5, - }; - }); - this.layer.updateModelData(modelData as IAttributeAndElements); - this.layerService.throttleRenderLayers(); + return { + vertices: positions, + indices, + size: 5, + }; } - /** - * load terrain texture & offset attribute z - */ - protected loadTerrainTexture(positions: number[], indices: number[]) { - const { - widthSegments = 1, - heightSegments = 1, - terrainTexture, - rgb2height = (r: number, g: number, b: number) => r + g + b, - } = this.layer.getLayerConfig() as IGeometryLayerStyleOptions; + private async loadTerrainImage(terrainTexture: string) { if (this.terrainImage) { // 若当前已经存在 image,直接进行偏移计算(LOD) if (this.terrainImageLoaded) { - this.translateVertex( - positions, - indices, - this.terrainImage, - widthSegments, - heightSegments, - rgb2height, - ); + return this.terrainImage; } else { - this.terrainImage.onload = () => { - this.translateVertex( - positions, - indices, - this.terrainImage, - widthSegments, - heightSegments, - rgb2height, - ); - }; + return new Promise((resolve) => { + this.terrainImage.onload = () => { + resolve(this.terrainImage); + }; + }); } } else { // 加载地形贴图、根据地形贴图对 planeGeometry 进行偏移 const terrainImage = new Image(); - this.terrainImage = terrainImage; terrainImage.crossOrigin = 'anonymous'; - terrainImage.onload = () => { - this.terrainImageLoaded = true; - // 图片加载完,触发事件,可以进行地形图的顶点计算存储 - setTimeout(() => this.layer.emit('terrainImageLoaded', null)); - this.translateVertex( - positions, - indices, - terrainImage, - widthSegments, - heightSegments, - rgb2height, - ); - }; - terrainImage.src = terrainTexture as string; + return new Promise((resolve) => { + terrainImage.onload = () => { + this.terrainImageLoaded = true; + resolve(terrainImage); + // 图片加载完,触发事件,可以进行地形图的顶点计算存储 + setTimeout(() => this.layer.emit('terrainImageLoaded', null)); + }; + terrainImage.src = terrainTexture as string; + }); } } diff --git a/packages/layers/src/geometry/models/sprite.ts b/packages/layers/src/geometry/models/sprite.ts index 45240b6c10..48306fcfe4 100644 --- a/packages/layers/src/geometry/models/sprite.ts +++ b/packages/layers/src/geometry/models/sprite.ts @@ -47,7 +47,7 @@ export default class SpriteModel extends BaseModel { } public planeGeometryUpdateTriangulation = () => { - const { spriteBottom = -100000 } = this.layer.getLayerConfig() as IGeometryLayerStyleOptions; + const { spriteBottom = -10 } = this.layer.getLayerConfig() as IGeometryLayerStyleOptions; const updateZ = this.spriteUpdate; const bottomZ = spriteBottom; const topZ = this.spriteTop; @@ -158,8 +158,8 @@ export default class SpriteModel extends BaseModel { public async initModels(): Promise { const { mapTexture, - spriteTop = 5000000, - spriteUpdate = 10000, + spriteTop = 300, + spriteUpdate = 10, spriteAnimate = SPRITE_ANIMATE_DIR.DOWN, } = this.layer.getLayerConfig() as IGeometryLayerStyleOptions; this.initUniformsBuffer(); diff --git a/packages/layers/src/geometry/shaders/plane_frag.glsl b/packages/layers/src/geometry/shaders/plane_frag.glsl index c12e0bf428..2c9b97231f 100644 --- a/packages/layers/src/geometry/shaders/plane_frag.glsl +++ b/packages/layers/src/geometry/shaders/plane_frag.glsl @@ -1,4 +1,3 @@ - uniform sampler2D u_texture; layout(std140) uniform commonUniforms { float u_opacity; @@ -13,12 +12,10 @@ out vec4 outputColor; #pragma include "picking" void main() { - // gl_FragColor = vec4(v_Color, u_opacity); - if(u_mapFlag > 0.0) { + if (u_mapFlag > 0.0) { outputColor = texture(SAMPLER_2D(u_texture), vec2(v_uv.x, 1.0 - v_uv.y)); outputColor.a *= u_opacity; } else { - // gl_FragColor = vec4(v_uv, 0.0, u_opacity); outputColor = vec4(v_Color, u_opacity); } outputColor.a *= v_clip; diff --git a/packages/layers/src/geometry/shaders/plane_vert.glsl b/packages/layers/src/geometry/shaders/plane_vert.glsl index d9f22980bc..7d921f0de4 100644 --- a/packages/layers/src/geometry/shaders/plane_vert.glsl +++ b/packages/layers/src/geometry/shaders/plane_vert.glsl @@ -1,4 +1,3 @@ - layout(location = ATTRIBUTE_LOCATION_POSITION) in vec3 a_Position; layout(location = ATTRIBUTE_LOCATION_COLOR) in vec3 a_Color; layout(location = ATTRIBUTE_LOCATION_UV) in vec2 a_Uv; @@ -16,17 +15,17 @@ out float v_clip; #pragma include "projection" #pragma include "picking" void main() { - v_Color = a_Color; - v_uv = a_Uv; + v_Color = a_Color; + v_uv = a_Uv; - vec4 project_pos = project_position(vec4(a_Position, 1.0)); + vec4 project_pos = project_position(vec4(a_Position, 1.0)); - v_clip = 1.0; - if(a_Position.z < u_terrainClipHeight) { - v_clip = 0.0; - } + v_clip = 1.0; + if (a_Position.z < u_terrainClipHeight) { + v_clip = 0.0; + } gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy, a_Position.z, 1.0)); - setPickingColor(a_PickingColor); + setPickingColor(a_PickingColor); } diff --git a/site/docs/api/other/plane.en.md b/site/docs/api/other/plane.en.md index eb5fb5dafc..1ddb637cf6 100644 --- a/site/docs/api/other/plane.en.md +++ b/site/docs/api/other/plane.en.md @@ -50,8 +50,8 @@ const layer = new GeometryLayer().shape('plane').style({ terrainTexture: 'https://gw.alipayobjects.com/mdn/rms_23a451/afts/img/A*eYFaRYlnnOUAAAAAAAAAAAAAARQnAQ', rgb2height: (r, g, b) => { - let h = -10000.0 + (r * 255.0 * 256.0 * 256.0 + g * 255.0 * 256.0 + b * 255.0) * 0.1; - h = h / 20 - 127600; + let h = (r * 255.0 * 256.0 * 256.0 + g * 255.0 * 256.0 + b * 255.0) * 0.1; + h = h / 200 - 12750; h = Math.max(0, h); return h; }, diff --git a/site/docs/api/other/plane.zh.md b/site/docs/api/other/plane.zh.md index ba7d413abc..30cfd9d7c0 100644 --- a/site/docs/api/other/plane.zh.md +++ b/site/docs/api/other/plane.zh.md @@ -50,8 +50,8 @@ const layer = new GeometryLayer().shape('plane').style({ terrainTexture: 'https://gw.alipayobjects.com/mdn/rms_23a451/afts/img/A*eYFaRYlnnOUAAAAAAAAAAAAAARQnAQ', rgb2height: (r, g, b) => { - let h = -10000.0 + (r * 255.0 * 256.0 * 256.0 + g * 255.0 * 256.0 + b * 255.0) * 0.1; - h = h / 20 - 127600; + let h = (r * 255.0 * 256.0 * 256.0 + g * 255.0 * 256.0 + b * 255.0) * 0.1; + h = h / 200 - 12750; h = Math.max(0, h); return h; }, diff --git a/site/docs/api/other/sprite.en.md b/site/docs/api/other/sprite.en.md index 0cfaa617b2..1b39dc79f1 100644 --- a/site/docs/api/other/sprite.en.md +++ b/site/docs/api/other/sprite.en.md @@ -24,8 +24,8 @@ let layer = new GeometryLayer() center: [120, 30], spriteCount: 120, spriteRadius: 10, - spriteTop: 2500000, - spriteUpdate: 20000, + spriteTop: 300, + spriteUpdate: 10, spriteScale: 0.6, }); ``` @@ -65,13 +65,11 @@ The number of particles generated by the layer, default is 100. #### spriteTop: number -The height range of particle movement, the default is 5000000.\ -🌟 It should be noted that under different map basemaps, the parameter values ​​of spriteTop/spriteUpdate need to be adjusted to achieve better results. The default value is adjusted on the AMap 1.0 map. +The height range of particle movement, the default is 300. #### spriteUpdate: number -The step size of particle motion refresh, expressed as motion speed, default is 10000.\ -🌟 It should be noted that under different map basemaps, the parameter values ​​of spriteTop/spriteUpdate need to be adjusted to achieve better results. The default value is adjusted on the AMap 1.0 map. +The step size of particle motion refresh, expressed as motion speed, default is 10. #### spriteScale: number diff --git a/site/docs/api/other/sprite.zh.md b/site/docs/api/other/sprite.zh.md index b21b08e492..5252c4702b 100644 --- a/site/docs/api/other/sprite.zh.md +++ b/site/docs/api/other/sprite.zh.md @@ -24,8 +24,8 @@ let layer = new GeometryLayer() center: [120, 30], spriteCount: 120, spriteRadius: 10, - spriteTop: 2500000, - spriteUpdate: 20000, + spriteTop: 300, + spriteUpdate: 10, spriteScale: 0.6, }); ``` @@ -65,13 +65,11 @@ SpriteGeometry 主要通过 style 方法设置位置、大小以及其他属性 #### spriteTop: number -粒子运动的高度范围,默认为 5000000. -🌟 需要注意的是,在不同的地图底图下,spriteTop/spriteUpdate 的参数值需要进行调整,才能得到较好的效果。默认值是在高德 1.0 的地图上调整的。 +粒子运动的高度范围,默认为 300. #### spriteUpdate: number -粒子运动刷新的步长,表现为运动速度、默认为 10000。 -🌟 需要注意的是,在不同的地图底图下,spriteTop/spriteUpdate 的参数值需要进行调整,才能得到较好的效果。默认值是在高德 1.0 的地图上调整的。 +粒子运动刷新的步长,表现为运动速度、默认为 10。 #### spriteScale: number diff --git a/site/examples/geometry/geometry/demo/rain.js b/site/examples/geometry/geometry/demo/rain.js index 7d9791ec18..ac60e90fb1 100644 --- a/site/examples/geometry/geometry/demo/rain.js +++ b/site/examples/geometry/geometry/demo/rain.js @@ -22,9 +22,9 @@ scene.on('loaded', () => { center: [120, 30], spriteCount: 120, spriteRadius: 10, - spriteTop: 2500000, - spriteUpdate: 20000, - spriteScale: 0.6, + spriteTop: 400, + spriteUpdate: 10, + spriteScale: 0.8, }); scene.addLayer(layer); }); diff --git a/site/examples/geometry/geometry/demo/snow.js b/site/examples/geometry/geometry/demo/snow.js index 23bc9c3e80..62d7607531 100644 --- a/site/examples/geometry/geometry/demo/snow.js +++ b/site/examples/geometry/geometry/demo/snow.js @@ -21,7 +21,8 @@ scene.on('loaded', () => { center: [120, 30], spriteCount: 60, spriteRadius: 10, - spriteTop: 2500000, + spriteTop: 300, + spriteUpdate: 5, }); scene.addLayer(layer); }); diff --git a/site/examples/geometry/geometry/demo/terrain.js b/site/examples/geometry/geometry/demo/terrain.js index 14864f0468..70a60c485a 100644 --- a/site/examples/geometry/geometry/demo/terrain.js +++ b/site/examples/geometry/geometry/demo/terrain.js @@ -27,8 +27,8 @@ scene.on('loaded', () => { terrainTexture: 'https://gw.alipayobjects.com/mdn/rms_23a451/afts/img/A*eYFaRYlnnOUAAAAAAAAAAAAAARQnAQ', rgb2height: (r, g, b) => { - let h = -10000.0 + (r * 255.0 * 256.0 * 256.0 + g * 255.0 * 256.0 + b * 255.0) * 0.1; - h = h / 20 - 127600; + let h = (r * 255.0 * 256.0 * 256.0 + g * 255.0 * 256.0 + b * 255.0) * 0.1; + h = h / 200 - 12750; h = Math.max(0, h); return h; }, @@ -40,24 +40,24 @@ scene.on('loaded', () => { modelData100; layer.on('terrainImageLoaded', () => { - modelData10 = layer.createModelData([], { + modelData10 = layer.layerModel.createModelData({ widthSegments: 10, heightSegments: 10, }); - modelData20 = layer.createModelData([], { + modelData20 = layer.layerModel.createModelData({ widthSegments: 20, heightSegments: 20, }); - modelData100 = layer.createModelData([], { + modelData100 = layer.layerModel.createModelData({ widthSegments: 100, heightSegments: 100, }); }); - scene.on('zoom', ({ value }) => { - const zoom = Math.floor(value); + scene.on('zoom', () => { + const zoom = Math.floor(scene.getZoom()); if (currentZoom !== zoom) { if (zoom > 13) { if (currentModelData !== '100x100') { @@ -77,6 +77,5 @@ scene.on('loaded', () => { } currentZoom = zoom; } - return ''; }); });