From 55efe0edabf24d8cfa222c0375ad52c9e7e1c867 Mon Sep 17 00:00:00 2001 From: huyang Date: Tue, 19 Dec 2023 16:19:42 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20imageLayer=E6=94=AF=E6=8C=81=E5=9B=BE?= =?UTF-8?q?=E5=83=8F=E5=A2=9E=E5=BC=BA=20(#2186)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: fillImage中uniform没对齐的bug * feat: imageLayer支持图片效果增强 (#2180) * chore: 删除imageLayer中的颜色叠加 * docs: 更新imageLayer.style中新增的图像增强的参数的文档 --------- Co-authored-by: huyang --- dev-demos/features/imageLayer/image.tsx | 67 ++++++++++++-- .../features/line/demos/linePopulation.tsx | 91 +++++++++---------- packages/layers/src/core/interface.ts | 5 +- packages/layers/src/image/models/image.ts | 22 ++--- .../layers/src/image/shaders/image_frag.glsl | 26 +++++- .../layers/src/image/shaders/image_vert.glsl | 6 +- .../site/docs/api/image_layer/style.zh.md | 4 + packages/utils/.umirc.ts | 2 + packages/utils/src/index.ts | 8 +- 9 files changed, 161 insertions(+), 70 deletions(-) diff --git a/dev-demos/features/imageLayer/image.tsx b/dev-demos/features/imageLayer/image.tsx index e6d1283033..c8cbb0b5a7 100644 --- a/dev-demos/features/imageLayer/image.tsx +++ b/dev-demos/features/imageLayer/image.tsx @@ -1,12 +1,22 @@ import { ImageLayer, Scene } from '@antv/l7'; import { GaodeMap } from '@antv/l7-maps'; import React, { useEffect } from 'react'; - +//加载外部脚本 +function addExternalScript(src) { + return new Promise((resolve, reject) => { + const script = document.createElement('script'); + script.src = src; + script.onload = () => resolve(); + script.onerror = () => reject(); + document.body.appendChild(script); + }); +} export default () => { useEffect(() => { const scene = new Scene({ id: 'map', pickBufferScale: 1.0, + renderer: 'device', map: new GaodeMap({ center: [121.268, 30.3628], pitch: 0, @@ -14,12 +24,18 @@ export default () => { zoom: 10, }), }); - + const layerStyle = { + brightness: 1.0, + gamma: 1.0, + color: '#ffffff', + opacity: 1.0, + saturation: 1.0, + contrast: 1.0, + }; const layer = new ImageLayer({}); layer.source( - // 'https://gw.alipayobjects.com/zos/rmsportal/FnHFeFklTzKDdUESRNDv.jpg', - // 'https://gw.alipayobjects.com/mdn/rms_816329/afts/img/A*0TVXSbkyKvsAAAAAAAAAAAAAARQnAQ', - 'https://gw.alipayobjects.com/mdn/rms_816329/afts/img/A*4k6vT6rUsk4AAAAAAAAAAAAAARQnAQ', + 'https://cdn.uino.cn/thing-earth-space/images/refraction.jpg', + // 'https://gw.alipayobjects.com/mdn/rms_816329/afts/img/A*4k6vT6rUsk4AAAAAAAAAAAAAARQnAQ', { parser: { type: 'image', @@ -27,10 +43,49 @@ export default () => { }, }, ); - + layer.style(layerStyle); scene.on('loaded', () => { scene.addLayer(layer); + scene.startAnimate(); + }); + const gui = new dat.GUI(); + gui.domElement.style.position = 'absolute'; + gui.domElement.style.top = '202px'; + gui.domElement.style.right = '220px'; + gui.add(layerStyle, 'brightness', 0, 2, 0.01).onChange((v) => { + layer.style({ + brightness: v, + }); + scene.render(); + }); + gui.add(layerStyle, 'saturation', 0, 2, 0.01).onChange((v) => { + layer.style({ + saturation: v, + }); + scene.render(); }); + gui.add(layerStyle, 'contrast', 0, 2, 0.01).onChange((v) => { + layer.style({ + contrast: v, + }); + scene.render(); + }); + gui.add(layerStyle, 'gamma', 0, 2, 0.01).onChange((v) => { + layer.style({ + gamma: v, + }); + scene.render(); + }); + gui.add(layerStyle, 'opacity', 0, 1, 0.01).onChange((v) => { + layer.style({ + opacity: v, + }); + scene.render(); + }); + + return () => { + gui.destroy(); + }; }, []); return (
{ - const script = document.createElement('script'); - script.src = src; - script.onload = () => resolve(); - script.onerror = () => reject(); - document.body.appendChild(script); - }); -} /** * @private * 点数据转线数据 @@ -32,10 +22,16 @@ function processPointToLine(data, minValue = 0) { let lines = Object.values(groupedPoints).map((points) => { let result = []; let lastLon = -500; - points = points.filter(v => { return parseFloat(v.properties.value) > minValue }) + points = points.filter((v) => { + return parseFloat(v.properties.value) > minValue; + }); for (let i = 0; i < points.length; i++) { if (Math.abs(points[i].geometry.coordinates[0] - lastLon) >= 2) { - const line = { type: 'Feature', geometry: { type: 'LineString', coordinates: [] }, properties: { heights: [] } }; + const line = { + type: 'Feature', + geometry: { type: 'LineString', coordinates: [] }, + properties: { heights: [] }, + }; result.push(line); } const line = result[result.length - 1]; @@ -45,10 +41,12 @@ function processPointToLine(data, minValue = 0) { line.properties.heights.push(coordinates[2]); lastLon = points[i].geometry.coordinates[0]; } - result = result.filter(v => { return v.geometry.coordinates.length > 1 }); + result = result.filter((v) => { + return v.geometry.coordinates.length > 1; + }); return result; }); - lines = lines.flat(1) + lines = lines.flat(1); // 创建包含多条线的新 GeoJSON 对象 return { type: 'FeatureCollection', @@ -88,28 +86,31 @@ export default () => { useEffect(() => { const scene = new Scene({ id: 'map', - renderer: process.env.renderer, + renderer: process.env.renderer, map: new GaodeMap({ pitch: 40, style: 'dark', center: [43.686824, -4.665872], zoom: 1.2, - token:"pk.eyJ1Ijoic2tvcm5vdXMiLCJhIjoiY2s4dDBkNjY1MG13ZTNzcWEyZDYycGkzMyJ9.tjfwvJ8G_VDmXoClOyxufg" - }) + token: + 'pk.eyJ1Ijoic2tvcm5vdXMiLCJhIjoiY2s4dDBkNjY1MG13ZTNzcWEyZDYycGkzMyJ9.tjfwvJ8G_VDmXoClOyxufg', + }), }); const guiConfig = { height: 30, minValue: 10000, color: '#0D5EFF', // opacity:1.0, - blend: 'additive' - } - let layer, originData,gui; + blend: 'additive', + }; + let layer, originData, gui; scene.on('loaded', () => { - fetch('https://static.observableusercontent.com/files/c517bc4710d3a5daf34549dde51fd3a0e457a62ce3113847cf804dd21b78a95dd0ecc0b975455c4c162f87e74e665e6994bb9bbd457a420e46d6b6179200b47d') - .then(res => res.text()) - .then(data => { + fetch( + 'https://static.observableusercontent.com/files/c517bc4710d3a5daf34549dde51fd3a0e457a62ce3113847cf804dd21b78a95dd0ecc0b975455c4c162f87e74e665e6994bb9bbd457a420e46d6b6179200b47d', + ) + .then((res) => res.text()) + .then((data) => { originData = csvToGeojson(data); const dataSource = processPointToLine(originData, guiConfig.minValue); layer = new LineLayer({ @@ -125,30 +126,28 @@ export default () => { .color(guiConfig.color); scene.addLayer(layer); }); - addExternalScript('https://cdn.uino.cn/thing-earth-space/libs/dat.gui.min.js').then(() => { - gui = new dat.GUI(); - gui.domElement.style.position = 'absolute'; - gui.domElement.style.top = '202px'; - gui.domElement.style.right = '220px'; - gui.add(guiConfig, "height", 1, 100, 0.1).onChange((v) => { - layer.style({ - vertexHeightScale: 0.01 * v - }); - scene.render(); - }); - gui.add(guiConfig, "minValue", 10000, 1000000, 1).onChange((v) => { - const dataSource = processPointToLine(originData, guiConfig.minValue); - layer.setData(dataSource); + gui = new dat.GUI(); + gui.domElement.style.position = 'absolute'; + gui.domElement.style.top = '202px'; + gui.domElement.style.right = '220px'; + gui.add(guiConfig, 'height', 1, 100, 0.1).onChange((v) => { + layer.style({ + vertexHeightScale: 0.01 * v, }); + scene.render(); + }); + gui.add(guiConfig, 'minValue', 10000, 1000000, 1).onChange((v) => { + const dataSource = processPointToLine(originData, guiConfig.minValue); + layer.setData(dataSource); + }); - gui.add(guiConfig, "blend", ['additive', 'normal']).onChange((v) => { - layer.setBlend(v); - }); - gui.addColor(guiConfig, "color").onChange((v) => { - layer.color(v); - scene.render(); - }); - }) + gui.add(guiConfig, 'blend', ['additive', 'normal']).onChange((v) => { + layer.setBlend(v); + }); + gui.addColor(guiConfig, 'color').onChange((v) => { + layer.color(v); + scene.render(); + }); }); return () => { gui.destroy(); diff --git a/packages/layers/src/core/interface.ts b/packages/layers/src/core/interface.ts index 6a1e3b02c0..48c4c38a87 100644 --- a/packages/layers/src/core/interface.ts +++ b/packages/layers/src/core/interface.ts @@ -22,7 +22,6 @@ export enum TextureBlend { NORMAL = 'normal', REPLACE = 'replace', } - /** * 基础图层类型定义 */ @@ -199,6 +198,10 @@ export interface IImageLayerStyleOptions extends IBaseLayerStyleOptions { clampHigh?: boolean; rampColors?: IColorRamp; colorTexture?: ITexture2D; + brightness?: number; + contrast?: number; + saturation ?: number; + gamma?: number; } export interface ICityBuildLayerStyleOptions { diff --git a/packages/layers/src/image/models/image.ts b/packages/layers/src/image/models/image.ts index a9616b8f34..c2b8e5091e 100644 --- a/packages/layers/src/image/models/image.ts +++ b/packages/layers/src/image/models/image.ts @@ -1,7 +1,6 @@ import type { IEncodeFeature, IModel, - IModelUniform, ITexture2D} from '@antv/l7-core'; import { AttributeType, @@ -13,24 +12,19 @@ import { RasterImageTriangulation } from '../../core/triangulation'; import ImageFrag from '../shaders/image_frag.glsl'; import ImageVert from '../shaders/image_vert.glsl'; import { ShaderLocation } from '../../core/CommonStyleAttribute'; - +import { defaultValue, rgb2arr } from '@antv/l7-utils'; export default class ImageModel extends BaseModel { protected texture: ITexture2D; - public getUninforms(): IModelUniform { - const commoninfo = this.getCommonUniformsInfo(); - const attributeInfo = this.getUniformsBufferInfo(this.getStyleAttribute()); - this.updateStyleUnifoms(); - return { - ...commoninfo.uniformsOption, - ...attributeInfo.uniformsOption, - } - } protected getCommonUniformsInfo(): { uniformsArray: number[]; uniformsLength: number; uniformsOption: { [key: string]: any; }; } { - const { opacity } = this.layer.getLayerConfig() as IImageLayerStyleOptions; + const { color = 'rgb(255,255,255)',opacity,brightness,contrast,saturation,gamma } = this.layer.getLayerConfig() as IImageLayerStyleOptions; + const colorArry = rgb2arr(color); const commonOptions = { - u_opacity: opacity || 1, - u_texture: this.texture, + u_opacity:defaultValue(opacity,1.0), + u_brightness:defaultValue(brightness,1.0), + u_contrast:defaultValue(contrast,1.0), + u_saturation:defaultValue(saturation,1.0), + u_gamma:defaultValue(gamma,1.0) }; this.textures = [this.texture] const commonBufferInfo = this.getUniformsBufferInfo(commonOptions); diff --git a/packages/layers/src/image/shaders/image_frag.glsl b/packages/layers/src/image/shaders/image_frag.glsl index 54dad1e6d3..697a764455 100644 --- a/packages/layers/src/image/shaders/image_frag.glsl +++ b/packages/layers/src/image/shaders/image_frag.glsl @@ -1,12 +1,36 @@ uniform sampler2D u_texture; layout(std140) uniform commonUniforms { - float u_opacity; + float u_opacity:1.0; + float u_brightness:1.0; + float u_contrast:1.0; + float u_saturation:1.0; + float u_gamma:1.0; }; in vec2 v_texCoord; out vec4 outputColor; +vec3 setContrast(vec3 rgb, float contrast) { + vec3 color = mix(vec3(0.5), rgb, contrast); + color = clamp(color, 0.0, 1.0); + return color; +} +vec3 setSaturation(vec3 rgb, float adjustment) { + const vec3 grayVector = vec3(0.2125, 0.7154, 0.0721); + vec3 intensity = vec3(dot(rgb, grayVector)); + vec3 color = mix(intensity, rgb, adjustment); + color = clamp(color, 0.0, 1.0); + return color; +} void main() { vec4 color = texture(SAMPLER_2D(u_texture),vec2(v_texCoord.x,v_texCoord.y)); + //brightness + color.rgb = mix(vec3(0.0, 0.0, 0.0), color.rgb, u_brightness); + //contrast + color.rgb = setContrast(color.rgb, u_contrast); + // saturation + color.rgb = setSaturation(color.rgb, u_saturation); + // gamma + color.rgb = pow(color.rgb, vec3(u_gamma)); outputColor = color; outputColor.a *= u_opacity; if(outputColor.a < 0.01) diff --git a/packages/layers/src/image/shaders/image_vert.glsl b/packages/layers/src/image/shaders/image_vert.glsl index e045683f29..dd48895f0c 100644 --- a/packages/layers/src/image/shaders/image_vert.glsl +++ b/packages/layers/src/image/shaders/image_vert.glsl @@ -2,7 +2,11 @@ layout(location = 0) in vec3 a_Position; layout(location = 14) in vec2 a_Uv; layout(std140) uniform commonUniforms { - float u_opacity; + float u_opacity:1.0; + float u_brightness:1.0; + float u_contrast:1.0; + float u_saturation:1.0; + float u_gamma:1.0; }; out vec2 v_texCoord; diff --git a/packages/site/docs/api/image_layer/style.zh.md b/packages/site/docs/api/image_layer/style.zh.md index 141d08dcfe..1f9b83b134 100644 --- a/packages/site/docs/api/image_layer/style.zh.md +++ b/packages/site/docs/api/image_layer/style.zh.md @@ -10,3 +10,7 @@ order: 4 | style | 类型 | 描述 | 默认值 | | ------- | -------- | ------------ | ------ | | opacity | `number` | 图形的透明度 | `1` | +| brightness | `number` | 图形的亮度 | `1` | +| contrast | `number` | 图形的对比度 | `1` | +| saturation | `number` | 图形的饱和度 | `1` | +| gamma | `number` | 图形的伽马值 | `1` | diff --git a/packages/utils/.umirc.ts b/packages/utils/.umirc.ts index 2fcef031ed..25e2aa03c0 100644 --- a/packages/utils/.umirc.ts +++ b/packages/utils/.umirc.ts @@ -95,6 +95,8 @@ export default defineConfig({ 'https://gw.alipayobjects.com/os/lib/antd/4.19.4/dist/antd.js', /** lodash */ 'https://gw.alipayobjects.com/os/lib/lodash/4.17.20/lodash.min.js', + //dat.gui + 'https://cdn.uino.cn/thing-earth-space/libs/dat.gui.min.js', ], // more config: https://d.umijs.org/config diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 742af53229..a01961be96 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -17,4 +17,10 @@ export * as Satistics from './statistics'; export * from './tileset-manager'; export * from './worker-helper'; export * from './workers/triangulation'; -export * from './interface/map' \ No newline at end of file +export * from './interface/map' +export function defaultValue(v1:any,v2:any){ + if(v1===undefined||v1===null){ + return v2; + } + return v1; +} \ No newline at end of file