maptalks+three实战项目 智慧城市项目

Posted aichitudousien

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了maptalks+three实战项目 智慧城市项目相关的知识,希望对你有一定的参考价值。

@

概述

如有不明白的可以加QQ:2354528292;wx: aichitudousien
更多教学视频请访问:https://space.bilibili.com/236087412

源码获取: https://item.taobao.com/item.htm?spm=a21dvs.23580594.0.0.3c3a645eIMTaft&ft=t&id=714967503431

经过上一次使用three.js做了个demo,这次使用maptalks+three+vue来继续开发一个demo玩玩
先看视频效果:

[video(video-bvzzjqdd-1615007935063)(type-csdn)(url-https://live.csdn.net/v/embed/156451)(image-https://vedu.csdnimg.cn/92e455d21899461ba223cc19b3530db5/snapshots/47b9fea390504faa91d59a2836f73dde-00002.jpg)(title-maptalks+three 智慧城市)]

搭建开发环境

使用的开发框架是vue-cli3.0, webgl使用three.js,地图使用maptalks,开发工具为vscode
搭建完成后的目录为

搭建maptalks+three场景

和上一个项目初始化场景一样,先创建一个ZThree的类,然后写入一个初始化方法initMapTalks

initMapTalks(option) 
    let config = 
      center: [113.31915199756622, 23.109087176037534],
      zoom: 17,
      pitch: 70,
      bearing: 180,

      centerCross: true,
      doubleClickZoom: false,
      baseLayer: new maptalks.TileLayer(\'tile\', 
        urlTemplate: \'https://s.basemaps.cartocdn.com/dark_all/z/x/y.png\',
        subdomains: [\'a\', \'b\', \'c\', \'d\']
      )
    ;
    option = Object.assign(config, option);
    let map = new maptalks.Map(this.id, option);

    let threeLayer = new ThreeLayer(\'t\', 
      forceRenderOnMoving: true,
      forceRenderOnRotating: true
    );

    threeLayer.addTo(map);

    ThreeLayer.prototype.coordinateToXYZ = function (coordinate, height = 0) 
      let z = this.distanceToVector3(height, height).x;
      let v = this.coordinateToVector3(coordinate, z)
      return [v.x, v.y, v.z];
    

    return threeLayer;
  
  • 实例化类
app = new ZThree("screen");

创建灯光

灯光代码和上次项目的代码一样,在博客中的智慧城市第一篇文章中查找

app.initLight();

此时我们看到的是已经成功创建好了地图

加载建筑模型

  1. 创建加载模型方法 loadBuilding
export function loadBuilding(threeLayer) 
  let features = buildingGeoJosn.features;

  let polygons = features.map(f => 
    let height = Number(f.properties.height);
    let polygon = maptalks.GeoJSON.toGeometry(f);
    polygon.setProperties(
      height,
    );
    return polygon;
  );

  let material = new THREE.ShaderMaterial(
    uniforms: bulidingShader.uniforms,
    vertexShader: bulidingShader.vs,
    fragmentShader: bulidingShader.fs,
    side: THREE.DoubleSide,
    transparent: true,
  )

  let mesh = threeLayer.toExtrudePolygons(polygons, 
    interactive: false
  , material);

  let meshs = [];

  let bufferGeometry = mesh.getObject3d().geometry;
  let geometry = new THREE.Geometry().fromBufferGeometry(bufferGeometry);

  let 
    vertices,
    faces,
    faceVertexUvs
   = geometry;
  for (let i = 0, len = faces.length; i < len; i++) 
    let 
      a,
      b,
      c
     = faces[i];
    let p1 = vertices[a],
      p2 = vertices[b],
      p3 = vertices[c];
    //top face
    if (p1.z > 0 && p2.z > 0 && p3.z > 0) 
      let uvs = faceVertexUvs[0][i];
      for (let j = 0, len1 = uvs.length; j < len1; j++) 
        uvs[j].x = 0;
        uvs[j].y = 0;
      
    
  
  mesh.getObject3d().geometry = new THREE.BufferGeometry().fromGeometry(geometry);
  bufferGeometry.dispose();
  geometry.dispose();
  meshs.push(mesh);

  threeLayer.addMesh(meshs);

  1. 在场景中调用此方法数据
loadBuilding(threeLayer);

数据可从此地址获得 https://www.openstreetmap.org/
此时我们可以看到的效果是,用的数据量小的做开发,后期数据自己替换即可

建筑贴图:

加载流光道路

流光道路使用MeshLineMaterial来实现,因为three.js的线条材质是没有宽度的,所以此处我们需要导入一个新的库THREE.MeshLine,当然可以使用管道tube来实现也是可以的,使用管道来实现请查看博客中的流光特效文章

export function loaderRoad(threeLayer, SpriteLine) 
  let texture = new THREE.TextureLoader().load(\'texture/road.png\');
  texture.anisotropy = 16;
  texture.wrapS = THREE.RepeatWrapping;
  texture.wrapT = THREE.RepeatWrapping;
  let camera = threeLayer.getCamera();

  let material = new MeshLineMaterial(
    map: texture,
    useMap: true,
    lineWidth: 13,
    sizeAttenuation: false,
    transparent: true,
    near: camera.near,
    far: camera.far
  );

  let features = roadGeoJosn.features;

  let meshs = []

  features.forEach(item => 
    if (item.geometry.type === \'LineString\' && item.properties?.highway === \'primary\') 
      let line = maptalks.GeoJSON.toGeometry(item);
      let mesh = new SpriteLine(line, 
        altitude: 0
      , material, threeLayer);
      meshs.push(mesh);
    
  )

  threeLayer.addMesh(meshs);

数据来源和建筑的是一个地址,添加好后在场景中引入即可,流光动画只要偏移贴图的uv就好了
此时的效果


道路流光的贴图

加载水系

水系使用Ocean来制作,具体的可以在github上搜索Ocean来看案例

export function loaderWater(threeLayer) 
  let polygons = maptalks.GeoJSON.toGeometry(waterGeoJosn);
  let oceans = polygons.map(p => 
    let ocean = new Ocean(p, 
      waterNormals: \'texture/waternormals.jpg\'
    , threeLayer)
    return ocean;
  );
  threeLayer.addMesh(oceans);

水系的贴图

此时我们看到的效果是

加载大厦模型

模型在网上随便找了一个,勉强能用
加载模型代码

export async function loaderModel(app, threeLayer, position) 
  let model = await app.loaderModel(
    "model/",
    \'building\'
  );
  let material = new THREE.ShaderMaterial(
    uniforms: modelShader.uniforms,
    vertexShader: modelShader.vs,
    fragmentShader: modelShader.fs,
    side: THREE.DoubleSide,
    transparent: true,
  )
  model.traverse(obj => 
    obj.material = material;
    obj.scale.set(1.5, 1.5, 1.5);
  )
  model.position.set(...position);
  model.rotateX(Math.PI / 2)
  threeLayer.addMesh(model);
  return model;

模型下载地址:https://download.csdn.net/download/qq_39503511/15611779
加载成功后看到的效果:

加载飞行线条

飞行线条也和上期代码中的开发方式一样的,通过三个点生成贝塞尔曲线,在添加贴图偏移就好了

export function loaderCurve(app, threeLayer, geojson = curve) 
  let points = geojson.features.map(item => 
    let coordinates = item.geometry.coordinates;
    return [new THREE.Vector3(...threeLayer.coordinateToXYZ(coordinates)), new THREE.Vector3(...threeLayer.coordinateToXYZ([-73.98565649986267, 40.74843959459197], 258))]
  )
  let meshs = []

  points.forEach((point, index) => 
    let c = point[0].clone().add(point[1].clone()).divideScalar(2);
    c.z += 4
    let curve = new THREE.QuadraticBezierCurve3(point[0], c, point[1])
    let points = curve.getPoints(80)
    let line = new THREE.CatmullRomCurve3(points)
    let tube = app.loaderTube(
      line,
      index % 2 === 0 ? tubeRedMaterial : tubeGreenMaterial
    );
    meshs.push(tube);
  )
  threeLayer.addMesh(meshs);

此时我们看到的效果是

创建圆锥体

export function loaderCone(threeLayer, geojson = cone) 
  let points = geojson.features.map(item => 
    let coordinates = item.geometry.coordinates;
    return threeLayer.coordinateToXYZ(coordinates, 100)
  )
  let geometry = new THREE.ConeGeometry(0.2, 0.4, 4);

  let meshs = []
  points.forEach(item => 
    let material = new THREE.MeshBasicMaterial(
      color: `rgb$rgb()`,
      transparent: true,
      opacity: 8,
      side: THREE.DoubleSide,
      depthWrite: false
    );
    let cone = new THREE.Mesh(geometry, material);
    cone.rotateX(-Math.PI / 2);
    cone.position.set(...item);
    meshs.push(cone);
  );
  threeLayer.addMesh(meshs);
  return meshs;

rgb函数是一个获取随机rbg颜色函数

export function rgb()  //rgb颜色随机
  let r = Math.floor(Math.random() * 256);
  let g = Math.floor(Math.random() * 256);
  let b = Math.floor(Math.random() * 256);
  let rgb = \'(\' + r + \',\' + g + \',\' + b + \')\';
  return rgb;

此时我们看到场景中的效果是

创建文本

最后再来创建一些基础的文本,使用精灵来创建

export function loaderText(app, threeLayer, geojson = text) 
  geojson.features.forEach(item => 
    let coordinates = item.geometry.coordinates;
    let position = threeLayer.coordinateToXYZ(coordinates, 200);
    let name = item.properties.name;
    let element = `
                    <div class="sprite-canvas">
                        <span class="sprite-layer">$name</span>
                    </div>`;
    app.addHtmlCanvas(
      parent: app.scene,
      position,
      element
    )
  )


addHtmlCanvas方法在上期已经贴出代码,通过html来生成canvas,然后在map使用就好
此时的效果:

最后我们给整个场景添加一个高光的后期处理就ok了

export function loaderBloom(threeLayer) 
  const params = 
    exposure: 1,
    bloomStrength: 1.5,
    bloomThreshold: 0,
    bloomRadius: 0,
    debug: false
  ;
  const renderer = threeLayer.getThreeRenderer();
  const size = threeLayer.getMap().getSize();
  threeLayer.composer = new EffectComposer(renderer);
  threeLayer.composer.setSize(size.width, size.height);

  const scene = threeLayer.getScene(),
    camera = threeLayer.getCamera();
  threeLayer.renderPass = new RenderPass(scene, camera);

  threeLayer.composer.addPass(threeLayer.renderPass);

  const bloomPass = threeLayer.bloomPass = new UnrealBloomPass(new THREE.Vector2(size.width, size.height));
  bloomPass.renderToScreen = true;
  bloomPass.threshold = params.bloomThreshold;
  bloomPass.strength = params.bloomStrength;
  bloomPass.radius = params.bloomRadius;
  threeLayer.composer.addPass(bloomPass);
  threeLayer.bloomEnable = true;

  threeLayer.getRenderer().renderScene = function () 
    const layer = this.layer;
    layer._callbackBaseObjectAnimation();
    this._syncCamera();
    const renderer = this.context,
      camera = this.camera,
      scene = this.scene;
    if (
      layer.bloomEnable &&
      layer.composer &&
      layer.composer.passes.length > 1
    ) 
      layer.composer.render(0);
      renderer.clearDepth();
      camera.layers.set(0);
      renderer.render(scene, camera);
    
    this.completeRender();
  ;


ok, 此时我们的整个场景就比之前亮了,我们在替换一下数据就成视频中的效果了

关于Three.js实现智慧城市我实现的一些特效

关于所有特效的集成效果可见知乎上我发的视频https://www.zhihu.com/zvideo/1277995319629037568

1、OD线(着色器实现)

2、透明墙

3、地面扩散粒子
4、地震波

5、发光扩散半球

6、发光旋转四棱锥

7、流动线

8、上升粒子

9、建筑线框
10、圆扩散

11、建筑物纹理流动

12、建筑流动着色器版本

13、粒子地面

14、雷达扫描(着色器实现)

15、flyto

16、mesh动画

17、仿真道路

18、格网地面
19、科技风线框

20、数字柱

21、流动箭头(gif显示有问题)

22、能量罩
23、扫描光带
24、无人机巡航特效
25、线框渲染
26、Xray透视特效
27、类似守望先锋能量护盾
28、光束模拟

以上是关于maptalks+three实战项目 智慧城市项目的主要内容,如果未能解决你的问题,请参考以下文章

vue使用maptalks地图+three加载video视频obj+mtl和gltf模型

关于Three.js实现智慧城市我实现的一些特效

关于Three.js实现智慧城市我实现的一些特效

关于Three.js实现智慧城市我实现的一些特效

关于Three.js实现智慧城市我实现的一些特效

专注智慧城市项目开发,智慧城市管理建设