Cesiumjs+Threejs开发,解决模型在地球背面显示的问题

2,048 阅读3分钟

欢迎转载,转载需带着文章出处链接~~,希望我的经验能帮到你,有问题可以点我头像加我微信 本文章仅对遇到的问题提供一个解决思路,对于迫切想要实现功能效果的人来说,希望能稍微沉下心观看,或者直接观看思路部分,或者直接观看完整代码 本文章的代码并不能直接拿来使用,完整代码只是本篇文章解决的问题需要用到的代码,如果直接使用可能会有报错,只要修改一下代码既可使用~~~

Cesiumjs+Threejs开发,解决模型在地球背面显示的问题

模型在地球背面时,本应该隐藏的模型,此时正在显示。

-参考-

CesiumJs+ThreeJs实测 cesium与three.js,解决three.js 高版本模型出不来的问题 CesiumJs获取中心坐标


-说明-

为什么出现了问题?

① Cesiumjs和Threejs集成的原理,通过定位两个DOM节点,使两个DOM重叠,将Threejs放到Cesiumjs上。
② 局部查看,Threejs的Mesh显示在对应的位置上。
③ 转动视角后,在地球背面本应该消失的模型,因为没有物体遮挡,所以模型还存在,出现了BUG。

-主要版本-

three.js r123,不仅限于这个版本 cesium.js 1.76,不仅限于这个版本


-思路-

1、查找代码相关位置 2、将Pointer替换为Mouse 3、event.pointerType无法获取,故直接跳过这步,将里边的方法拿出来


-过程-

1、Cesium和Three集成

官方给出的cesium和threejs结合的方案,在这里将对loop代码进行规整,代码如下所示。

function loop() {
    requestAnimationFrame(loop); //loop
    renderCesium();  //渲染cesium
    renderThree();   //渲染three
    renderCamera(); // 视角调整
}

2、获取中心点坐标

在renderCamera视角调整代码处,修改增加获取当前视角下中心坐标点的代码,这里代码写成2部分,方便代码观看,callback回调在步骤3。

// 处理三维模型显示隐藏问题
function getCenterPosition(callback) {
    var viewer = cesium.viewer;
    var result = viewer.camera.pickEllipsoid(new Cesium.Cartesian2(viewer.canvas.clientWidth / 2, viewer.canvas
        .clientHeight / 2));
    var curPosition = Cesium.Ellipsoid.WGS84.cartesianToCartographic(result);
    var lon = curPosition.longitude * 180 / Math.PI;
    var lat = curPosition.latitude * 180 / Math.PI;
    var height = getHeight(); // 获取高度
    var now = getCurrentExtent(); // 获取当前视角下的经纬度的范围
    
    callback(lon,lat,height,now);

    return {
        lon: lon,
        lat: lat,
        height: height
    };
}

3、动态修改Threejs模型显隐

在renderCamera视角调整代码处,调用该方法,对模型进行显示隐藏处理

getCenterPosition((lon, lat, height, now) => {
    let hei = now.height;
    // 地球背面模型隐藏 
    for (let k in modelDict) {
        let item = modelDict[k];
        var pos = item.getPosition();
        let lon1 = pos.lon;
        let lat1 = pos.lat;
        let hei1 = pos.hei;
        if (Math.abs(lon1 - lon) > 300) {
            if (lon1 < 0) {
                lon1 += 360
            } else {
                lon += 360
            }
        }
        if (Math.abs(lon1 - lon) > 90 + hei1 / 100000 || Math.abs(lat1 - lat) > 60) {
            item.setVisible(false);
        } else {
            item.setVisible(true);
        }
        // 优化显示
        if (hei - hei1 < 2000000 && Math.abs(lon - lon1) < 6 && Math.abs(lat - lat1) < 6) {
            item.setVisible(true);
        } else {
            item.setVisible(false);
        }
    }
});

-完整代码-

function loop() {
  requestAnimationFrame(loop);
  renderCesium();  //渲染cesium
  renderThree();   //渲染three
  renderCamera();

  // nowDate += 1000 * 60
  // var utc=Cesium.JulianDate.fromDate(new Date(nowDate)); // 获取当前时间
  // cesium.viewer.clockViewModel.currentTime = Cesium.JulianDate.addHours(utc,0,new Cesium.JulianDate()); // 设置时间,第二个是时差
}

function renderCesium() {
  cesium.viewer.render();
}

function renderThree() {
  var width = ThreeContainer.clientWidth;
  var height = ThreeContainer.clientHeight;
  three.renderer.setSize(width, height);
  three.renderer.render(three.scene, three.camera);
}


function renderCamera() {
  // register Three.js scene with Cesium
  three.camera.fov = Cesium.Math.toDegrees(cesium.viewer.camera.frustum.fovy) // ThreeJS FOV is vertical
  three.camera.updateProjectionMatrix();

  // Clone Cesium Camera projection position so the
  // Three.js Object will appear to be at the same place as above the Cesium Globe

  three.camera.matrixAutoUpdate = false;
  var cvm = cesium.viewer.camera.viewMatrix;
  var civm = cesium.viewer.camera.inverseViewMatrix;
  three.camera.lookAt(new THREE.Vector3(0, 0, 0));
  three.camera.matrixWorld.set(
    civm[0], civm[4], civm[8], civm[12],
    civm[1], civm[5], civm[9], civm[13],
    civm[2], civm[6], civm[10], civm[14],
    civm[3], civm[7], civm[11], civm[15]
  );
  three.camera.matrixWorldInverse.set(
    cvm[0], cvm[4], cvm[8], cvm[12],
    cvm[1], cvm[5], cvm[9], cvm[13],
    cvm[2], cvm[6], cvm[10], cvm[14],
    cvm[3], cvm[7], cvm[11], cvm[15]
  );


  var width = ThreeContainer.clientWidth;
  var height = ThreeContainer.clientHeight;
  var aspect = width / height;
  three.camera.aspect = aspect;
  three.camera.updateProjectionMatrix();

  getCenterPosition((lon, lat, height, now) => {
    let hei = now.height;
    // 地球背面模型隐藏 
    for (let k in modelDict) {
      let item = modelDict[k];
      var pos = item.getPosition();
      let lon1 = pos.lon;
      let lat1 = pos.lat;
      let hei1 = pos.hei;
      if (Math.abs(lon1 - lon) > 300) {
        if (lon1 < 0) {
          lon1 += 360
        } else {
          lon += 360
        }
      }
      if (Math.abs(lon1 - lon) > 90 + hei1 / 100000 || Math.abs(lat1 - lat) > 60) {
        item.setVisible(false);
      } else {
        item.setVisible(true);
      }
      // 优化显示
      if (hei - hei1 < 2000000 && Math.abs(lon - lon1) < 6 && Math.abs(lat - lat1) < 6) {
        item.setVisible(true);
      } else {
        item.setVisible(false);
      }
    }
  });
}
// 处理三维模型显示隐藏问题
function getCenterPosition(callback) {
    var viewer = cesium.viewer;
    var result = viewer.camera.pickEllipsoid(new Cesium.Cartesian2(viewer.canvas.clientWidth / 2, viewer.canvas
        .clientHeight / 2));
    var curPosition = Cesium.Ellipsoid.WGS84.cartesianToCartographic(result);
    var lon = curPosition.longitude * 180 / Math.PI;
    var lat = curPosition.latitude * 180 / Math.PI;
    var height = getHeight();
    var now = getCurrentExtent();
    
    callback(lon,lat,height,now);

    return {
        lon: lon,
        lat: lat,
        height: height
    };
}
/* 获取camera高度  */
function getHeight() {
    var viewer = cesium.viewer;
    if (viewer) {
        var scene = viewer.scene;
        var ellipsoid = scene.globe.ellipsoid;
        var height = ellipsoid.cartesianToCartographic(viewer.camera.position).height;
        return height;
    }
}
function getCurrentExtent() {
    // 范围对象
    var extent = {};

    // 得到当前三维场景
    var viewer = cesium.viewer;
    var scene = viewer.scene;

    // 得到当前三维场景的椭球体
    var ellipsoid = scene.globe.ellipsoid;
    var canvas = scene.canvas;

    // canvas左上角
    var car3_lt = viewer.camera.pickEllipsoid(new Cesium.Cartesian2(0, 0), ellipsoid);

    // canvas右下角
    var car3_rb = viewer.camera.pickEllipsoid(new Cesium.Cartesian2(canvas.width, canvas.height), ellipsoid);

    // 当canvas左上角和右下角全部在椭球体上
    if (car3_lt && car3_rb) {
        var carto_lt = ellipsoid.cartesianToCartographic(car3_lt);
        var carto_rb = ellipsoid.cartesianToCartographic(car3_rb);
        extent.xmin = Cesium.Math.toDegrees(carto_lt.longitude);
        extent.ymax = Cesium.Math.toDegrees(carto_lt.latitude);
        extent.xmax = Cesium.Math.toDegrees(carto_rb.longitude);
        extent.ymin = Cesium.Math.toDegrees(carto_rb.latitude);
    }

    // 当canvas左上角不在但右下角在椭球体上
    else if (!car3_lt && car3_rb) {
        var car3_lt2 = null;
        var yIndex = 0;
        do {
            // 这里每次10像素递加,一是10像素相差不大,二是为了提高程序运行效率
            yIndex <= canvas.height ? yIndex += 10 : canvas.height;
            car3_lt2 = viewer.camera.pickEllipsoid(new Cesium.Cartesian2(0, yIndex), ellipsoid);
        } while (!car3_lt2);
        var carto_lt2 = ellipsoid.cartesianToCartographic(car3_lt2);
        var carto_rb2 = ellipsoid.cartesianToCartographic(car3_rb);
        extent.xmin = Cesium.Math.toDegrees(carto_lt2.longitude);
        extent.ymax = Cesium.Math.toDegrees(carto_lt2.latitude);
        extent.xmax = Cesium.Math.toDegrees(carto_rb2.longitude);
        extent.ymin = Cesium.Math.toDegrees(carto_rb2.latitude);
    }

    // 获取高度
    extent.height = Math.ceil(viewer.camera.positionCartographic.height);
    return extent;
}