diff --git a/debug/gl/terrain-mask.html b/debug/gl/terrain-mask.html index f96c2c79d7..15318de935 100644 --- a/debug/gl/terrain-mask.html +++ b/debug/gl/terrain-mask.html @@ -31,6 +31,7 @@ "zoom": 13, "pitch": 10, "bearing": 0, + renderer: 'gl', queryTerrainInMapEvents: false }); @@ -51,6 +52,7 @@ const terrain = { debug: true, + shader: 'lit', type: "mapbox", urlTemplate: `https://{s}.tiles.mapbox.com/v4/mapbox.terrain-rgb/{z}/{x}/{y}.pngraw?access_token=${token}`, subdomains: ["a", "b", "c", "d"] @@ -65,11 +67,11 @@ type: "Polygon", coordinates: [ [ - [94.50812103, 29.45095163, 3000], - [94.59012103, 29.45095163, 3000], - [94.59012103, 29.40095163, 3000], - [94.50812103, 29.40095163, 3000], - [94.50812103, 29.45095163, 3000] + [94.50812103, 29.45095163, 0], + [94.59012103, 29.45095163, 0], + [94.59012103, 29.40095163, 0], + [94.50812103, 29.40095163, 0], + [94.50812103, 29.45095163, 0] ] ] }, @@ -99,11 +101,10 @@ renderPlugin: { dataConfig: { type: "fill", + altitudeOffset: 3000 }, sceneConfig: {}, type: "terrain-flat-mask", - }, - symbol: { } } ] diff --git a/packages/gl/src/layer/terrain/TerrainLitPainter.js b/packages/gl/src/layer/terrain/TerrainLitPainter.js index 1e98fdaa27..6d0f645e24 100644 --- a/packages/gl/src/layer/terrain/TerrainLitPainter.js +++ b/packages/gl/src/layer/terrain/TerrainLitPainter.js @@ -105,11 +105,16 @@ class TerrainLitPainter extends TerrainPainter { const material = new reshader.pbr.StandardMaterial(matInfo); const mesh = new reshader.Mesh(geo, material); mesh.properties.matVer = this._matVer; + if (!mesh.uniforms.flatMask) { + const emptyTexture = this.getEmptyTexture(); + mesh.setUniform('flatMask', emptyTexture); + } const defines = mesh.defines; defines['HAS_UV_FLIP'] = 1; defines['HAS_TERRAIN_NORMAL'] = 1; defines['HAS_MAP'] = 1; defines['HAS_LAYER_OPACITY'] = 1; + defines['HAS_TERRAIN_FLAT_MASK'] = 1; mesh.defines = defines; // mesh.setUniform('terrainTileResolution', tileInfo.res); this.prepareMesh(mesh, tileInfo, terrainImage); @@ -126,7 +131,10 @@ class TerrainLitPainter extends TerrainPainter { mesh.properties.matVer = this._matVer; } if (tileImage.skin) { - mesh.material.set('skinTexture', tileImage.skin); + mesh.material.set('skinTexture', tileImage.skin.color[0]); + } + if (tileImage.mask) { + mesh.setUniform('flatMask', tileImage.mask.color[0]); } mesh.setUniform('polygonOpacity', 1.0); // const { skirtOffset, skirtCount } = mesh.properties; diff --git a/packages/reshader.gl/src/pbr/glsl/standard.vert b/packages/reshader.gl/src/pbr/glsl/standard.vert index 3d1e342aa0..674251014d 100644 --- a/packages/reshader.gl/src/pbr/glsl/standard.vert +++ b/packages/reshader.gl/src/pbr/glsl/standard.vert @@ -4,8 +4,13 @@ precision highp float; attribute vec3 aPosition; -#if defined(HAS_MAP) +#if defined(HAS_MAP) || defined(HAS_TERRAIN_FLAT_MASK) attribute vec2 aTexCoord; + #include +#endif + +#if defined(HAS_MAP) + uniform vec2 uvOrigin; uniform vec2 uvScale; uniform vec2 uvOffset; diff --git a/packages/reshader.gl/src/pbr/wgsl/standard_vert.wgsl b/packages/reshader.gl/src/pbr/wgsl/standard_vert.wgsl index 51defac84f..702578b99b 100644 --- a/packages/reshader.gl/src/pbr/wgsl/standard_vert.wgsl +++ b/packages/reshader.gl/src/pbr/wgsl/standard_vert.wgsl @@ -8,6 +8,7 @@ #include #endif #include +#include struct VertexInput { #ifdef POSITION_IS_INT @@ -16,8 +17,11 @@ struct VertexInput { @location($i) aPosition: vec3f, #endif - #if HAS_MAP + #if HAS_MAP || HAS_TERRAIN_FLAT_MASK @location($i) aTexCoord: vec2f, + #endif + #if HAS_MAP + #ifdef HAS_I3S_UVREGION @location($i) uvRegion: vec4f, #endif diff --git a/packages/reshader.gl/src/shaderlib/glsl/output.vert b/packages/reshader.gl/src/shaderlib/glsl/output.vert index c9fcada760..35fe7804d9 100644 --- a/packages/reshader.gl/src/shaderlib/glsl/output.vert +++ b/packages/reshader.gl/src/shaderlib/glsl/output.vert @@ -69,7 +69,11 @@ mat4 getPositionMatrix() { } #ifdef HAS_MIN_ALTITUDE -uniform float minAltitude; + uniform float minAltitude; +#endif + +#ifdef HAS_TERRAIN_FLAT_MASK + uniform sampler2D flatMask; #endif vec4 getPosition(vec3 aPosition) { @@ -84,6 +88,15 @@ vec4 getPosition(vec3 aPosition) { #ifdef HAS_TERRAIN_ALTITUDE POSITION.z += aTerrainAltitude * 100.0; #endif + #ifdef HAS_TERRAIN_FLAT_MASK + vec2 uv = aTexCoord; + uv.y = 1.0 - uv.y; + vec4 encodedHeight = texture2D(flatMask, uv); + if (length(encodedHeight) < 2.0) { + float maskHeight = decodeFloat32(encodedHeight); + POSITION.z = min(POSITION.z, maskHeight); + } + #endif #ifdef HAS_MIN_ALTITUDE POSITION.z += minAltitude * 100.0; #endif diff --git a/packages/reshader.gl/src/shaderlib/wgsl/common_pack_float.ts b/packages/reshader.gl/src/shaderlib/wgsl/common_pack_float.ts index cb8fefceb5..809133f8cb 100644 --- a/packages/reshader.gl/src/shaderlib/wgsl/common_pack_float.ts +++ b/packages/reshader.gl/src/shaderlib/wgsl/common_pack_float.ts @@ -67,6 +67,34 @@ fn common_encodeDepth(depth: f32) -> vec4f { fn common_decodeDepth(pack: vec4f) -> f32 { return pack.r + pack.g / 255.0; } + +// https://cloud.tencent.com/developer/ask/sof/103481834 +fn encodeFloat32(f: f32) -> vec4f { + var e = 5.0; + + var F = abs(f); + var Sign = step(0.0, -f); + var Exponent = floor(log2(F)); + var Mantissa = (exp2(- Exponent) * F); + Exponent = floor(log2(F) + 127.0) + floor(log2(Mantissa)); + var rgba = vec4(0.0); + rgba[0] = 128.0 * Sign + floor(Exponent*exp2(-1.0)); + rgba[1] = 128.0 * mod(Exponent,2.0) + mod(floor(Mantissa*128.0),128.0); + rgba[2] = floor(mod(floor(Mantissa*exp2(23.0 -8.0)),exp2(8.0))); + rgba[3] = floor(exp2(23.0)*mod(Mantissa,exp2(-15.0))); + return rgba / 255.0; +} + +fn decodeFloat32(rgba: vec4f) -> f32 { + rgba *= 255.0; + var Sign = 1.0 - step(128.0,rgba[0])*2.0; + var Exponent = 2.0 * mod(rgba[0],128.0) + step(128.0,rgba[1]) - 127.0; + var Mantissa = mod(rgba[1],128.0)*65536.0 + rgba[2]*256.0 +rgba[3] + float(0x800000); + var Result = Sign * exp2(Exponent) * (Mantissa * exp2(-23.0 )); + return Result; +} + + `; const frag = vert; diff --git a/packages/reshader.gl/src/shaderlib/wgsl/output.ts b/packages/reshader.gl/src/shaderlib/wgsl/output.ts index ce5d6d108e..169442dd2b 100644 --- a/packages/reshader.gl/src/shaderlib/wgsl/output.ts +++ b/packages/reshader.gl/src/shaderlib/wgsl/output.ts @@ -31,6 +31,11 @@ const vert = /* wgsl */` @group(0) @binding($b) var altitudeUniforms: AltitudeUniforms; #endif +#ifdef HAS_TERRAIN_FLAT_MASK + @group(0) @binding($b) var flatMask: texture_2d; + @group(0) @binding($b) var flatMaskSampler: sampler; +#endif + fn getPositionMatrix(input: VertexInput, vertexOutput: ptr, positionMatrix: mat4x4f) -> mat4x4f { var worldMatrix: mat4x4f; #ifdef HAS_INSTANCE @@ -79,6 +84,15 @@ fn getPosition(inputPosition: vec3f, vertexInput: VertexInput) -> vec4f { #ifdef HAS_TERRAIN_ALTITUDE outputPosition.z += vertexInput.aTerrainAltitude * 100.0; #endif + #ifdef HAS_TERRAIN_FLAT_MASK + var uv = aTexCoord; + uv.y = 1.0 - uv.y; + let encodedHeight = textureSample(flatMask, flatMaskSampler, uv); + if (length(encodedHeight) < 2.0) { + float maskHeight = decodeFloat32(encodedHeight); + outputPosition.z = min(outputPosition.z, maskHeight); + } + #endif #ifdef HAS_MIN_ALTITUDE outputPosition.z += altitudeUniforms.minAltitude * 100.0; #endif diff --git a/packages/vt/src/layer/layer/VectorTileLayer.ts b/packages/vt/src/layer/layer/VectorTileLayer.ts index 75abdf06e8..2df1806870 100644 --- a/packages/vt/src/layer/layer/VectorTileLayer.ts +++ b/packages/vt/src/layer/layer/VectorTileLayer.ts @@ -1291,6 +1291,9 @@ class VectorTileLayer extends maptalks.TileLayer { ) { throw new Error(`Invalid filter at ${i} : ${JSON.stringify(filter)}`); } + if (!styles[i].symbol) { + styles[i].symbol = {}; + } //TODO 如果定义了renderPlugin就必须定义symbol } } diff --git a/packages/vt/test/integration/fixtures/terrain/terrain-lit-mask/expected.png b/packages/vt/test/integration/fixtures/terrain/terrain-lit-mask/expected.png new file mode 100644 index 0000000000..2b00d505a6 Binary files /dev/null and b/packages/vt/test/integration/fixtures/terrain/terrain-lit-mask/expected.png differ diff --git a/packages/vt/test/integration/fixtures/terrain/terrain-lit-mask/index.js b/packages/vt/test/integration/fixtures/terrain/terrain-lit-mask/index.js new file mode 100644 index 0000000000..1e53df74b7 --- /dev/null +++ b/packages/vt/test/integration/fixtures/terrain/terrain-lit-mask/index.js @@ -0,0 +1,49 @@ +const data = { + type: 'FeatureCollection', + features: [ + { type: 'Feature', geometry: { type: 'Polygon', coordinates: [ + [[91.14478,29.696272], [91.14778,29.696272], [91.14778,29.690272], [91.14478,29.690272], [91.14478,29.696272]]] + }, properties: { type: 1 } } + ] + +}; + +const style = [ + { + filter: true, + renderPlugin: { + type: 'fill', + dataConfig: { + type: 'fill' + } + }, + symbol: { + polygonFill: '#f00', + polygonOpacity: 0.5 + } + }, + { + filter: true, + renderPlugin: { + type: 'terrain-flat-mask', + dataConfig: { + type: 'fill', + altitudeOffset: 3500 + } + }, + symbol: {} + } +]; + +module.exports = { + style, + data, + lit: true, + renderingCount: 10, + view: { + center: [91.14478,29.708272], + zoom: 12, + pitch: 20, + bearing: 0, + } +}; diff --git a/packages/vt/test/integration/fixtures/terrain/terrain-mask/expected.png b/packages/vt/test/integration/fixtures/terrain/terrain-mask/expected.png new file mode 100644 index 0000000000..bbc7413347 Binary files /dev/null and b/packages/vt/test/integration/fixtures/terrain/terrain-mask/expected.png differ diff --git a/packages/vt/test/integration/fixtures/terrain/terrain-mask/index.js b/packages/vt/test/integration/fixtures/terrain/terrain-mask/index.js new file mode 100644 index 0000000000..8ec3ca2026 --- /dev/null +++ b/packages/vt/test/integration/fixtures/terrain/terrain-mask/index.js @@ -0,0 +1,48 @@ +const data = { + type: 'FeatureCollection', + features: [ + { type: 'Feature', geometry: { type: 'Polygon', coordinates: [ + [[91.14478,29.696272], [91.14778,29.696272], [91.14778,29.690272], [91.14478,29.690272], [91.14478,29.696272]]] + }, properties: { type: 1 } } + ] + +}; + +const style = [ + { + filter: true, + renderPlugin: { + type: 'fill', + dataConfig: { + type: 'fill' + } + }, + symbol: { + polygonFill: '#f00', + polygonOpacity: 0.5 + } + }, + { + filter: true, + renderPlugin: { + type: 'terrain-flat-mask', + dataConfig: { + type: 'fill', + altitudeOffset: 3500 + } + }, + symbol: {} + } +]; + +module.exports = { + style, + data, + renderingCount: 10, + view: { + center: [91.14478,29.708272], + zoom: 12, + pitch: 20, + bearing: 0, + } +}; diff --git a/packages/vt/test/integration/terrain.spec.js b/packages/vt/test/integration/terrain.spec.js index 6c8b1700a1..fb14ec4a2b 100644 --- a/packages/vt/test/integration/terrain.spec.js +++ b/packages/vt/test/integration/terrain.spec.js @@ -23,6 +23,8 @@ const DEFAULT_VIEW = { } }; +const TEST_CANVAS = document.createElement('canvas'); + describe('vector tile on terrain integration specs', () => { let map, container, server; const containerSize = 128; @@ -51,6 +53,7 @@ describe('vector tile on terrain integration specs', () => { options.lights = DEFAULT_VIEW.lights; } options.devicePixelRatio = 1; + options.renderer = 'canvas'; const limit = style.renderingCount || 6; map = new maptalks.Map(container, options); @@ -76,18 +79,29 @@ describe('vector tile on terrain integration specs', () => { fadeAnimation: false // shader: 'lit' }; + if (style.lit) { + terrain.shader = 'lit'; + } const group = new GroupGLLayer('group', [layer], { terrain }); group.addTo(map); + const terrainLayer = group.getTerrainLayer(); let count = 0; let ended = false; terrainLayer.on('terrainreadyandrender', () => { count++; - const canvas = map.getRenderer().canvas; + const mapCanvas = map.getRenderer().canvas; const expectedPath = style.expected; if (!ended && count >= limit) { ended = true; + + const canvas = TEST_CANVAS; + canvas.width = mapCanvas.width; + canvas.height = mapCanvas.height; + const ctx = canvas.getContext('2d'); + ctx.drawImage(mapCanvas, 0, 0); + //比对测试 match(canvas, expectedPath, (err, result) => { if (err) { diff --git a/packages/vt/test/specs/update.vector.symbol.spec.js b/packages/vt/test/specs/update.vector.symbol.spec.js index 25d26340fd..9950a72999 100644 --- a/packages/vt/test/specs/update.vector.symbol.spec.js +++ b/packages/vt/test/specs/update.vector.symbol.spec.js @@ -73,6 +73,7 @@ describe('vector layers symbol update specs', () => { count++; }); let updated = false; + let doneCalled = false; group.on('layerload', () => { if (count >= 1 && !updated) { const pixel = readPixel(layer.getRenderer().canvas, x + 20, y); @@ -83,11 +84,12 @@ describe('vector layers symbol update specs', () => { markerHeight: 60 }); updated = true; - } else if (updated && count >= 4) { + } else if (updated && count >= 4 && !doneCalled) { const pixel = readPixel(renderer.canvas, x + 20, y); //中心点往外40,能读到像素了 assert.deepEqual(pixel, [255, 0, 0, 255]); assert(!partialUpdate); + doneCalled = true; done(); } }); @@ -123,6 +125,7 @@ describe('vector layers symbol update specs', () => { }); let updated = false; let partialUpdate = false; + let doneCalled = false; layer.on('partialupdate', () => { partialUpdate = true; }); @@ -151,12 +154,13 @@ describe('vector layers symbol update specs', () => { }, ]); updated = true; - } else if (updated && count >= 3) { + } else if (updated && count >= 3 && !doneCalled) { const pixel = readPixel(renderer.canvas, x + 20, y); assert.deepEqual(pixel, [0, 255, 0, 255]); const pixel1 = readPixel(renderer.canvas, x, y); assert.deepEqual(pixel1, [255, 0, 0, 255]); assert(!partialUpdate); + doneCalled = true; done(); } });