Threejs实现3d地球记录(2)

2、添加地球光圈

地球光圈图:
Threejs实现3d地球记录(2)_第1张图片

//地球光圈
function createSprite(R) {
  var textureLoader = new THREE.TextureLoader();
  var texture = textureLoader.load(spriteImg);//加载纹理贴图
  // 创建精灵材质对象SpriteMaterial
  var spriteMaterial = new THREE.SpriteMaterial({
    map: texture, //设置贴图
    transparent: true,//开启透明
    // opacity: 0.5,//通过透明度整体调节光圈
  });
  var sprite = new THREE.Sprite(spriteMaterial);
  sprite.scale.set(R * 3.0, R * 3.0, 1);//缩放精灵
  return sprite
}

function createEarth(R) {
  var earthGroup = new THREE.Group();//地球组对象
  earthGroup.add(createSphereMesh(R));//球体Mesh插入earthGroup中
  earthGroup.add(createSprite(R));//地球光圈
  });
  return earthGroup;
}

效果:
Threejs实现3d地球记录(2)_第2张图片

3、绘制国家边界线

(1)、经纬度转球面坐标

公式如下:
Threejs实现3d地球记录(2)_第3张图片

function lon2xyz(R, longitude, latitude) { //longitude:经度角度值,latitude:纬度角度值
  var lon = longitude * Math.PI / 180;//转弧度值
  var lat = latitude * Math.PI / 180;
  lon = -lon;// three.js坐标系z坐标轴对应经度-90度,而不是90度

  // 经纬度坐标转球面坐标计算公式
  var x = R * Math.cos(lat) * Math.cos(lon);
  var y = R * Math.sin(lat);
  var z = R * Math.cos(lat) * Math.sin(lon);
  // 返回球面坐标
  return {
    x: x,
    y: y,
    z: z,
  };
}

(2)、边界线创建

通过BufferGeometry构建一个几何体并传入顶点数据,通过LineLoop模型渲染几何体,连点成线,但是设置线模型Line对应线材质LineBasicMaterial的线宽属性.lineWidth,是无效的。可使用拓展库Line2解决此问题。
Threejs实现3d地球记录(2)_第4张图片

// pointArr:边界坐标
function line(pointArr) {
  var geometry = new THREE.BufferGeometry(); //创建一个Buffer类型几何体对象
  var vertices = new Float32Array(pointArr);//创建顶点数据
  //BufferAttribute这个类用于存储与BufferGeometry相关联的 attribute(例如顶点位置向量,面片索引,法向量,颜色值,UV坐标以及任何自定义 attribute )。 利用 BufferAttribute,可以更高效的向GPU传递数据。
  var attribue = new THREE.BufferAttribute(vertices, 3); //表示一个顶点的xyz坐标
  geometry.attributes.position = attribue; // 设置几何体attributes属性的位置属性

  var material = new THREE.LineBasicMaterial({
    color: 0x00aaaa
  });
  var line = new THREE.LineLoop(geometry, material);//线条模型对象,首尾顶点连线
  return line;
}

(3)、创建国家边界线

  • 了解world.json数据结构
{
    "type": "FeatureCollection",
    "features": [
        {
            "type": "Feature",
            "properties": {
            "name": "...",//国家名称
            "childNum": 1
            }
            "geometry": {                
              "type": "Polygon",//国家轮廓由一个多边形轮廓构成
              "coordinates": [//国家边界经纬度坐标
                [
                  [74.54140625000002, 37.02216796875],
                    [74.00185546875002, 36.823095703125]
                //   ...等经纬度坐标数据
                ]
              ]
            }
        },    {
            "type": "Feature",
            "properties": {
            "name": "...",//国家名称
            "childNum": 1
            }
            "geometry": {            
              "type": "MultiPolygon",// MultiPolygon:表示一个国家有多个轮廓构成
              "coordinates": [
                [
                    //国家的轮廓1
                  [
                    [17.57958984375, -8.099023437500009],
                    [17.643359375000017, -8.090722656250009]
                    //   ...等经纬度坐标数据
                  ]
                ],
                [
                    //国家的轮廓2
                  [
                    [12.255273437500023, -5.746484374999994]
                    //...等经纬度坐标数据
                  ]
                ]
              ]
            }
          }
    ]
 }

‘world.json’文件包含每个国家的边界线经纬度数据,所有国家边界坐标数据存放于worldjson.features中,每个国家下对应的"Polygon"代表该国家有一个封闭轮廓,"MultiPolygon代表该国家有多个封闭轮廓。

// 创建国家边界线
function countryLine(R, polygonArr) {
  var group = new THREE.Group();// 组对象mapGroup是所有国家边界父对象
  polygonArr.forEach(polygon => {
    var pointArr = [];//边界线顶点坐标
    polygon[0].forEach(elem => {
      // 经纬度转球面坐标
      var coord = lon2xyz(R, elem[0], elem[1])
      pointArr.push(coord.x, coord.y, coord.z);
    });
    group.add(line(pointArr));
  });
  return group;
}

function createEarth(R) {
  var earthGroup = new THREE.Group();//地球组对象
  earthGroup.add(createSphereMesh(R));//球体Mesh插入earthGroup中
  earthGroup.add(createSprite(R));//地球光圈

  worldjson.features.forEach(function (country) {
    if (country.geometry.type === "Polygon") {
      // 把"Polygon"和"MultiPolygon"的geometry.coordinates数据结构处理为一致
      country.geometry.coordinates = [country.geometry.coordinates];
    }
    var line = countryLine(R * 1.002, country.geometry.coordinates);//国家边界
    earthGroup.add(line);
  });

  return earthGroup;
}

效果:
Threejs实现3d地球记录(2)_第5张图片

4、生成国家球面Mash

(1)、多边形轮廓内生成等间距点阵

  • 在多边形轮廓最大经纬度范围形成的矩形内通过嵌套for循环生成等间距点阵
    1)、找到多边形轮廓最大经纬度范围
//   经纬度坐标进行排序
function minMax(arr) {
  arr.sort(compareNum);
  // 通过向两侧取整,把经纬度的方位稍微扩大
  return [Math.floor(arr[0]), Math.ceil(arr[arr.length - 1])]
}
// 排序规则
function compareNum(num1, num2) {
  if (num1 < num2) {
    return -1;
  } else if (num1 > num2) {
    return 1;
  } else {
    return 0;
  }
}

2)、在矩形范围内批量生成等间距的网格顶点数据

  var lonArr = [];//polygon的所有经度坐标
  var latArr = [];//polygon的所有纬度坐标
  polygon.forEach(elem => {  //polygon表示多边形轮廓的数组
    lonArr.push(elem[0])
    latArr.push(elem[1])
  });
  //计算polygon所有经纬度返回的极大值、极小值
  var [lonMin, lonMax] = minMax(lonArr);
  var [latMin, latMax] = minMax(latArr);
  // 经纬度极小值和极大值构成一个矩形范围,在矩形范围内生成等间距顶点
  var interval = 3; //各点之间的距离
  var row = Math.ceil((lonMax - lonMin) / interval);
  var col = Math.ceil((latMax - latMin) / interval)
  var rectPointsArr = [];//生成均匀间隔的矩形网格数据
  for (var i = 0; i < row + 1; i++) {
    for (var j = 0; j < col + 1; j++) {
      rectPointsArr.push([lonMin + i * interval, latMin + j * interval])
    }
  }
  • 判断点是否在轮廓内,删除多余点
    判断是否在轮廓内可使用开源库point-in-polygon,github地址:https://github.com/substack/point-in-polygon
var polygonPointsArr = [];//轮廓内的网格顶点数据
  rectPointsArr.forEach(function (coord) {
    if (pointInPolygon(coord, polygon)) {//判断点是否位于多边形中
      polygonPointsArr.push(coord)
    }
  })
  // 返回多边形polygon边界坐标和polygon内等间距顶点坐标
  return [...polygon, ...polygonPointsArr];

完整方法:

function gridPoint(polygon) {
  var lonArr = [];
  var latArr = [];
  polygon.forEach(elem => {
    lonArr.push(elem[0])
    latArr.push(elem[1])
  });
  var [lonMin, lonMax] = minMax(lonArr);
  var [latMin, latMax] = minMax(latArr);
  var interval = 3; 
  var row = Math.ceil((lonMax - lonMin) / interval);
  var col = Math.ceil((latMax - latMin) / interval)
  var rectPointsArr = [];
  for (var i = 0; i < row + 1; i++) {
    for (var j = 0; j < col + 1; j++) {
      rectPointsArr.push([lonMin + i * interval, latMin + j * interval])
    }
  }
  var polygonPointsArr = [];
  rectPointsArr.forEach(function (coord) {
    if (pointInPolygon(coord, polygon)) {
      polygonPointsArr.push(coord)
    }
  })
  return [...polygon, ...rectPointsArr];
}
  • 创建模型,显示生成的等间距点阵,其中需要进行合并几何体操作,原因为一个国家可能存在多个轮廓,需将同一个国家的轮廓合并放在同一个几何体中,合并操作使用THREE扩展库mergeBufferGeometries。
function countryMesh(R, polygonArr) {//polygonArr:国家轮廓经纬度
  var geometryArr = [];
  polygonArr.forEach(obj => {
    var polygon = obj[0];//获取多边形轮廓数据
    var pointsArr = gridPoint(polygon);
    var spherePointsArr = [];
    pointsArr.forEach((item, i) => {
      // 经纬度坐标转球面坐标
      var pos = lon2xyz(R, item[0], item[1])
      spherePointsArr.push(pos.x, pos.y, pos.z)
    });
    var geometry = new THREE.BufferGeometry();
    // 设置几何体顶点位置坐标
    geometry.attributes.position = new THREE.BufferAttribute(new Float32Array(spherePointsArr), 3)
    geometryArr.push(geometry);//geometryArr:一个国家多个轮廓,每个轮廓对应的所有几何体
  });
  var newGeometry = null;
  if (geometryArr.length == 1) {
    newGeometry = geometryArr[0];//如果一个国家只有一个多边形轮廓,不用进行几何体合并操作
  } else {
    newGeometry = mergeBufferGeometries(geometryArr);
  }
  var pointsMaterial = new THREE.PointsMaterial({
    color: 0x1cfb05,
    size: 1,
  });
  var points = new THREE.Points(newGeometry, pointsMaterial);
  return points
}
  • 在地球模型中添加国家轮廓点阵
function createEarth(R) {
  var earthGroup = new THREE.Group();//地球组对象
  earthGroup.add(createSphereMesh(R));
  earthGroup.add(createSprite(R));//地球光圈


  worldjson.features.forEach(function (country) {
    if (country.geometry.type === "Polygon") {
      // 把"Polygon"和"MultiPolygon"的geometry.coordinates数据结构处理为一致
      country.geometry.coordinates = [country.geometry.coordinates];
    }
    var line = countryLine(R * 1.002, country.geometry.coordinates);//国家边界
    var points= countryMesh(R * 1.001, country.geometry.coordinates);//国家轮廓点阵
    earthGroup.add(line);//国家边界集合插入earthGroup中
    earthGroup.add(points);//国家轮廓点阵集合插入earthGroup中
  });
  return earthGroup;
}

效果:
Threejs实现3d地球记录(2)_第6张图片

你可能感兴趣的:(3d,javascript,开发语言)