【threejs】在Cesiumjs内使用threejs渲染场景

文章介绍了如何使用Cesium和Three.js库创建一个交互式的3D地图控制器,包括初始化场景、处理GeoJSON数据、计算视图范围和相机设置,以及实现对象的动态旋转和缩放功能。
摘要由CSDN通过智能技术生成

本方法来自Cesium官网,细节改动来自Github社区

创建该类即可,具体细节备注有写

	/* eslint-disable one-var */
import * as THREE from "three";
import { TransformControls } from "three/examples/jsm/controls/TransformControls";
import { WorkSpace } from "./Constructions/workSpace";//一个threejs的工作单元
import { xxgeojson } from "../localJson/datacache/xxgeojson ";
// 区域geojson
import { getThreeCss3dRender, getThreeRender, getThreeScene } from "...someconfig.js";
//初始化threej方法 项目统一方法和用于获取render scene
export default class CesiumThreeController {
  isDestory = false
  playNode = true
  cesium = {
    viewer: null
  };
  three = {
    renderer: null,
    camera: null,
    scene: null,
    control: null
  };
  minWGS84 = [115.56936458615716, 39.284100766866445];
  maxWGS84 = [117.10745052365716, 41.107831235616445];
  sceneContainer;
  // cesium 容器
  cesiumContainer = document.getElementById("cesiumContainer");
  _3Dobjects = []; // Could be any Three.js object mesh
  workSpace
  setThreeObjects
  constructor(viewer, setThreeObjects, data = xxgeojson ) {
    this.setThreeObjects = setThreeObjects
    this.initData(data)
      .then(data => this.initConfig(data))
      .then(() => this.initView(viewer));
  }
  initData = async datasource => {
    // 获取三维场景参考边界 数据来自geojson  
    // 不要也行  但是形式上需要一个边界计算相机角度朝向,理论上不要出界的好,会影响图像精度
    let data = null;
    if (typeof datasource == "string") {
      data = await fetch(datasource).then(res => {
        return res.json();
      });
    } else {
      data = datasource;
    }

    return data;
  };
  initConfig = data => {
    // 通过获取的在线geojson 计算最小外接矩形边界 计算相机朝向、位置 
    let coordinates = data.features[0].geometry.coordinates[0];
    // 边界
    let maxLng = coordinates[0][0],
      maxLat = coordinates[0][1];
    let minLng = coordinates[0][0],
      minLat = coordinates[0][1];
    coordinates.forEach(([lng, lat]) => {
      if (lng > maxLng) maxLng = lng;
      if (lat > maxLat) maxLat = lat;
      if (lng < minLng) minLng = lng;
      if (lat < minLat) minLat = lat;
    });
    const minWGS84 = [minLng, minLat];
    const maxWGS84 = [maxLng, maxLat];

    // 中心
    const center = [(maxLng + minLng) / 2, (maxLat + minLat) / 2];

    const config = {
      minWGS84,
      maxWGS84,
      center,
      geojson: data
    };
    Object.assign(this, config);
    return this;
  };
  initView = viewer => {
    // 初始化cesium,这里使用新建类接收的viewer
    // 初始化 threejs 
    // 初始化相机更新动画anime
    this.cesium.viewer = viewer;
    this.initThree();
    this.init3DObject();
    this.anime = window.requestAnimationFrame(this.loop)
  };
  initThree = () => {
    let { three, minWGS84, maxWGS84, _3Dobjects } = this;
    let fov = 45;
    let width = window.innerWidth;
    let height = window.innerHeight;
    let aspect = width / height;
    let near = 1;
    let far = 10 * 1000 * 1000;
    // three.scene = new THREE.Scene();
    three.scene = getThreeScene()
    three.camera = new THREE.PerspectiveCamera(fov, aspect, near, far);


    // renderer
    // three.renderer = new THREE.WebGLRenderer({ alpha: true, logarithmicDepthBuffer: true ,antialias:true});
    three.renderer = getThreeRender();
    let Amlight = new THREE.AmbientLight(0xffffff, 2);
    three.renderer.domElement.style.position = "absolute";
    three.renderer.domElement.style.top = "0";
    three.renderer.domElement.style.pointerEvents = "none";

    three.scene.add(Amlight);
    // 注意这里,直接把three容器(canvas 添加到 cesium中,在cesium的canvas之下),
    // 这样的话,两个canvas才会重叠起来。
    this.cesium.viewer.cesiumWidget.canvas.parentElement.appendChild(
      three.renderer.domElement
    );
    // 2d renderer
    // let labelRenderer = new CSS3DRenderer();
    let labelRenderer = getThreeCss3dRender()

    labelRenderer.setSize(window.innerWidth, window.innerHeight);
    labelRenderer.domElement.style.position = 'absolute';
    labelRenderer.domElement.style.top = '0px';
    labelRenderer.domElement.style.pointerEvents = "none";
    this.cesium.viewer.cesiumWidget.canvas.parentElement.appendChild(
      labelRenderer.domElement
    );
    three.labelRenderer = labelRenderer

    let control = new TransformControls(
      three.camera,
      three.renderer.domElement
    );
    three.control = control;

    const sceneContainer = new THREE.Group();//因为每个3d对象都需要单独计算位置,所以用一个group统一处理
    three.scene.add(sceneContainer);
    three.scene.add(control);

    const _3DOB2 = new _3DObject();//注册3d对象,用于后续刷新相机位置时显示隐藏模型
    _3DOB2.threeMesh = sceneContainer;
    _3DOB2.minWGS84 = minWGS84;
    _3DOB2.maxWGS84 = maxWGS84;
    _3Dobjects.push(_3DOB2);//设计上可存储多个threejs的3d对象,但是本类使用sceneContainer总览,所以从始至终只有一个_3DOB2

    this.sceneContainer = sceneContainer;
  };

  init3DObject = () => {
    // 创建子类3d对象
    this.createThreeObject();
  };
  loop = () => {
    this.anime = window.requestAnimationFrame(this.loop);
    if (this.isDestory) {

      cancelAnimationFrame(this.anime)
      let { renderer, camera, control, scene,labelRenderer } = this.three
      scene.remove(camera)
      scene.remove(control)

      this.cesium.viewer.cesiumWidget.canvas.parentElement.removeChild(
        renderer.domElement//threejs render
      );
      this.cesium.viewer.cesiumWidget.canvas.parentElement.removeChild(
        labelRenderer.domElement//threejs 3d/2drender
      );
      renderer.dispose()

    }

    if (this.playNode == false) return
    this.renderCesium();//cesium刷新
    this.renderThreeObj();
  };

  renderCesium = () => {
    // 自动的 有强制刷新要求再刷新
    // this.cesium.viewer.render();
  };
  renderThreeObj = () => {
    // threejs更新
    let { three, cesium, _3Dobjects, minWGS84, maxWGS84 } = this;
    // register Three.js scene with Cesium
    three.camera.fov = Cesium.Math.toDegrees(cesium.viewer.camera.frustum.fovy); // ThreeJS FOV is vertical
    // three.camera.updateProjectionMatrix();
    let cartToVec = function (cart) {
      return new THREE.Vector3(cart.x, cart.y, cart.z);
    };

    // Configure Three.js meshes to stand against globe center position up direction
    for (let id in _3Dobjects) {
      minWGS84 = _3Dobjects[id].minWGS84;
      maxWGS84 = _3Dobjects[id].maxWGS84;
      // convert lat/long center position to Cartesian3
      let center = Cesium.Cartesian3.fromDegrees(
        (minWGS84[0] + maxWGS84[0]) / 2,
        (minWGS84[1] + maxWGS84[1]) / 2
      );
      // get forward direction for orienting model
      let centerHigh = Cesium.Cartesian3.fromDegrees(
        (minWGS84[0] + maxWGS84[0]) / 2,
        (minWGS84[1] + maxWGS84[1]) / 2,
        1
      );
      // use direction from bottom left to top left as up-vector
      let bottomLeft = cartToVec(
        Cesium.Cartesian3.fromDegrees(minWGS84[0], minWGS84[1])
      );
      let topLeft = cartToVec(
        Cesium.Cartesian3.fromDegrees(minWGS84[0], maxWGS84[1])
      );
      let latDir = new THREE.Vector3()
        .subVectors(bottomLeft, topLeft)
        .normalize();
      // configure entity position and orientation
      _3Dobjects[id].threeMesh.position.copy(center);
      _3Dobjects[id].threeMesh.lookAt(centerHigh.x, centerHigh.y, centerHigh.z);
      _3Dobjects[id].threeMesh.up.copy(latDir);
    }
    // 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;
    let cvm = cesium.viewer.camera.viewMatrix;
    let civm = cesium.viewer.camera.inverseViewMatrix;

    // 注意这里,经大神博客得知,three高版本这行代码需要放在 three.camera.matrixWorld 之前
    // 忘记在哪看的了 LOL
    three.camera.lookAt(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]
    );

    // 设置three宽高
    let width = cesiumContainer.clientWidth;
    let height = cesiumContainer.clientHeight;

    let aspect = width / height;
    three.camera.aspect = aspect;
    three.camera.updateProjectionMatrix();
    three.renderer.setSize(width, height);
    three.labelRenderer.setSize(width, height)
    three.renderer.clear();
    three.renderer.render(three.scene, three.camera);
    three.labelRenderer.render(three.scene, three.camera);
  };
  
  
  // };
  createThreeObject = () => {
    // 从这里初始化threejs场景
    // 这里的workSpace是一个工作单元,用于搭建threejs场景
    let {
      three,
      cesium,
      _3Dobjects,
      minWGS84,
      maxWGS84,
      sceneContainer//3D对象的容器
    } = this;
    let { scene, control, camera } = three
  
    const workSpace = new WorkSpace(scene, control, camera, sceneContainer, this.setThreeObjects)
   
    this.workSpace = workSpace

  };



  /***
   * 暂停 开始和销毁在anime内控制  
   */
  // 暂停更新
  pause = () => {
    this.playNode = false
  }
  // 开始更新
  play = () => {
    this.playNode = true
  }
  // 销毁
  destory = () => {
    this.isDestory = true


  }
}


// 子类 代理threejs的3d对象用
class _3DObject {
  constructor() {
    // THREEJS 3DObject.mesh
    this.threeMesh = null;
    // location bounding box
    this.minWGS84 = null;
    this.maxWGS84 = null;
  }
}


/**
 * 调整3d对象位置角度 调试用方法  推荐在threejs中调整
 */
function transforme3DGroup() {
  let stander = "p";
  window.addEventListener("keydown", ({ key }) => {
    console.log(key);
    if (key == "r") stander = "r";
    if (key == "p") stander = "p";
    if (key == "h") stander = "h";

    if (stander == "p") {
      if (key == "ArrowRight") scene.position.x -= 10;
      if (key == "ArrowLeft") scene.position.x += 10;
      if (key == "ArrowUp") scene.position.y -= 10;
      if (key == "ArrowDown") scene.position.y += 10;
    } else if (stander == "r") {
      if (key == "ArrowUp") scene.rotation.y -= 0.01;
      if (key == "ArrowDown") scene.rotation.y += 0.01;
    } else if (stander == "h") {
      if (key == "ArrowUp") scene.position.z -= 10;
      if (key == "ArrowDown") scene.position.z += 10;
    }

    if (key == "v") console.log(scene);
  });
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鸢_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值