threebox基于geojson数据加载二维面并使用纹理贴图

文章讲述了在Mapbox上利用Threebox库和GeoJSON数据创建二维面的过程。作者首先尝试使用extrusion方法,但因性能问题转向使用ShapeGeometry。通过计算坐标偏差和转换经纬度为世界坐标,解决了放大时的闪烁和纹理紊乱问题,实现了理想的加载速度和效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

需求:mapbox基于geojson数据生成的二维面虽然可以通过fill-pattern属性去使用图片填充,但是效果并不好(放大缩小时贴图会重新渲染),因此想使用threebox基于geojson数据生成二维面叠加到mapbox地图上,并使用纹理贴图。

threebox库地址如下:

https://github.com/jscastro76/threebox

在研究了threebox目前的代码以后,发现只定义了line、extrusion、tube、Object3D等,首先尝试使用extrusion进行加载,将高度设为0感觉可以实现所需效果,代码如下:

let material = new THREE.MeshPhongMaterial({
    color: 0x3E4E6F,
    side: THREE.DoubleSide
});
let loader = new THREE.FileLoader();
loader.load('../data/staticRoad.json', (result) => {
    let res = JSON.parse(result)
    res.features.forEach((data) => {
        let polygon = turf.polygon(data.geometry.coordinates)
        let center = turf.center(polygon).geometry.coordinates
        let extrusion = tb.extrusion({
            coordinates: data.geometry.coordinates,
            geometryOptions: { curveSegments: 1, bevelEnabled: false, depth: 0 },
            materials: material
        });
        extrusion.setCoords([center[0], center[1], 0]);
        tb.add(extrusion)
    })
})

将depth设为0发现确实可以实现纯色填充,然后替换纹理材质

let texture = new THREE.TextureLoader().load("images/road.png");
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set(10, 10);
let material = new THREE.MeshPhongMaterial({
    map: texture,
    side: THREE.DoubleSide
});

这样子虽然实现了效果,加载效果如下图,但是由于geojson数据较大加载太慢了(猜测还是因为构建的是三维的extrusion),响应时间在十几秒甚至几十秒,于是放弃了这种做法。

 查看了threejs相关文档,发现在threejs中,一般使用ShapeGeometry构建二维面,因此想到在threebox中使用ShapeGeometry构建二维面。

尝试代码:

// 纹理加载器
let textureLoader = new THREE.TextureLoader(); 
let texture = textureLoader.load("images/road.png");
// 设置阵列模式
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
// uv两个方向纹理重复数量
texture.repeat.set(10, 10);
let picMaterial = new THREE.MeshPhongMaterial({
  map: texture,
  side: THREE.DoubleSide
});
let loader = new THREE.FileLoader();
loader.load('../data/staticRoad.json', function (result) {
  let res = JSON.parse(result)
  res.features.forEach((data) => {
    let coordinates = data.geometry.coordinates[0]

    let shape = new THREE.Shape();
    let coords = tb.projectToWorld([coordinates[0][0], coordinates[0][1]])
    shape.moveTo(coords.x, coords.y);
    for (let i = 1; i < coordinates.length; i++) {
      let point = tb.projectToWorld([coordinates[i][0], coordinates[i][1]])
      shape.lineTo(point.x, point.y);
    }

    const geometry = new THREE.ShapeGeometry(shape);
    const mesh = new THREE.Mesh(geometry, picMaterial);
    tb.add(mesh)
  })
});

使用tb.projectToWorld将经纬度转换成世界坐标,虽然加载速度提升很明显,但是这样加载出来以后在放大时会导致面闪动,且纹理贴图紊乱,效果十分不好。

 

 在各种尝试之后发现,为了避免放大面时的闪动和贴图紊乱问题,改变ShapeGeometry构建方式。首先计算所加载面的绝对中心点并转换成世界坐标,再分别将每个坐标点转换成世界坐标并计算出和绝对中心点的偏差值,根据偏差值来构建面,最终将面移动到中心点位置。代码如下:

// 纹理加载器
let textureLoader = new THREE.TextureLoader();
let texture = textureLoader.load("images/road.png");
// 设置阵列模式
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
// uv两个方向纹理重复数量
texture.repeat.set(10, 10);
let material = new THREE.MeshPhongMaterial({
  map: texture,
  side: THREE.DoubleSide
});

let loader = new THREE.FileLoader();
loader.load('../data/staticRoad.json', function (result) {
  let res = JSON.parse(result)
  res.features.forEach((data) => {
    let coordinates = data.geometry.coordinates[0]
    let polygon = turf.polygon([coordinates]);
    var shape = new THREE.Shape();
    let center = turf.center(polygon).geometry.coordinates;
    let centerWorld = tb.projectToWorld([center[0], center[1]]);
    
    for(let i = 0;i<coordinates.length;i++){
      let point = tb.projectToWorld([coordinates[i][0], coordinates[i][1]]);
      if(i === 0 ){
        shape.moveTo(point.x-centerWorld.x, point.y-centerWorld.y);
      }else{
        shape.lineTo(point.x-centerWorld.x, point.y-centerWorld.y);
      }
    }
    
    let geometry = new THREE.ShapeGeometry(shape);
    let mesh = new THREE.Mesh(geometry, material);
    mesh.position.copy(centerWorld)
  })
});

注意计算偏差值时用该点坐标减去中心点坐标。

大功告成,加载速度完美~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值