CesiumJs结合ThreeJs(实测)

7,737 阅读5分钟

首发:鲸讯微信小程序
作者:鲸林向海工作室
原帖:blog.csdn.net/zhishiqu/ar…

跟着原贴做了一下threecesium的结合,代码稍微改动了点,记录一下。完整代码附在最后。

1、思路

1.要把cesiumjsthreejs的场景同时显示,所以需要创建两个容器,分别用于显示cesium的场景和three的场景,两个场景要重合; 2.将cesiumthree的渲染频率调成一致; 3.将cesiumthree的相机位置角度等调成一致。 4.将three里的图形位置调整为在地球表面

2、具体实现

2.1初始化cesium

cesium初始化时,要将它的自动渲染关掉(即useDefaultRenderLoop属性调整为false

            cesium.viewer = new Cesium.Viewer("cesiumContainer", {
                useDefaultRenderLoop: false,           //关闭自动渲染
                selectionIndicator: false,
                homeButton: false,
                sceneModePicker: false,
                navigationHelpButton: false,
                animate: false,
                timeline: false,
                fullscreenButton: false,
                navigationInstructionsInitiallyVisible: false,
                allowTextureFilterAnisotropic: false,
                contextOptions: {
                    webgl: {
                        alpha: false,
                        antialias: true,
                        preserveDrawingBuffer: true,
                        failIfMajorPerformanceCaveat: false,
                        depth: true,
                        stencil: false,
                        anialias: false
                    }
                },
                targetFrameRate: 60,
                resolutionScale: 0.1,
                orderIndependentTranslucency: true,
                // creditContainer:"CreditDisplay",
                // imageeryProvider: googlemap,   //谷歌地图
                baseLayerPicker: true,
                geocoder: false,
                automaticallyTrackDataSourceClocks: false,
                dataSources: null,
                clock: null,
                terrainShadows: Cesium.ShadowMode.DISABLED
            });

2.2初始化three.js

           let fov = 45;
            let width = window.innerWidth;
            let height = window.innerHeight;
            let aspect = width / height;
            let near = 1;
            let far = 10 * 1000 * 1000; // needs to be far to support Cesium's world-scale rendering

            three.scene = new THREE.Scene();
            three.camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
            three.renderer = new THREE.WebGLRenderer({ alpha: true });
            // let axis=new THREE.AxesHelper(1000*1000*1000);
            // three.scene.add(axis);
            ThreeContainer.appendChild(three.renderer.domElement);

2.3调整three和cesium的渲染频率

手动开启cesiumthree的渲染,并放进一个渲染频率里。

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

        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);
        }

2.4调整相机一致

这里使用的cesium的相机为主相机,使three的相机与cesium保持一致即可。 由于相机的位置一直在变换,所以讲相机位置调整放进每一帧的渲染中。

        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.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.camera.lookAt(new THREE.Vector3(0, 0, 0));

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

2.5加入要展示的图形

这里加入一个cesium的图形polygon,再加入一个three的球体,以及一个three的12面体。 cesiumy朝上,threez朝上,所以要进行坐标的变换,使three也变成y朝上。其次,three的图形要想放置在地球表面,需要z轴指向天空,垂直于地球表面,我们需要调整three图形的方向。方便起见,我们创建一个组group,将所有加入的three的图形都放进这个group中,然后对group进行位置变换。 需要注意的是,three中添加的球体需要加入光源才能看到,否则只能看到一个黑色的球体,我们把光源一同放入group中,确保光源的位置足以照射到球体。

 function _3DObject() {
     this.threeMesh = null;
     this.minWGS84 = null;
     this.maxWGS84 = null;
 }
  function init3DObject() {
     let entity = {
                name: 'Polygon',
                polygon: {
                    hierarchy: Cesium.Cartesian3.fromDegreesArray([
                        minWGS84[0], minWGS84[1],
                        maxWGS84[0], minWGS84[1],
                        maxWGS84[0], maxWGS84[1],
                        minWGS84[0], maxWGS84[1]
                    ]),
                    material: Cesium.Color.RED.withAlpha(0.1)
                }
            }
     let Polypon = cesium.viewer.entities.add(entity);
     let doubleSideMaterial = new THREE.MeshNormalMaterial({
         side: THREE.DoubleSide
     });

     geometry = new THREE.SphereGeometry(1, 32, 32);
     let sphere = new THREE.Mesh(geometry, new THREE.MeshPhongMaterial({ color: 0xffffff, side: THREE.DoubleSide }));   //12面体
     // sphere.scale.set(5000,5000,5000);
     // sphere.position.z+=15000;
     // translate "up" in Three.js space so the "bottom" of the mesh is the handle
     sphere.scale.set(5000, 5000, 5000);
     sphere.uuid = "sphere";
     var sphereYup = new THREE.Group();
     sphereYup.add(sphere)
     three.scene.add(sphereYup); // don’t forget to add it to the Three.js scene manually
     sphereYup.position.set(ce.x, ce.y, ce.z);
     _3DOB = new _3DObject();
     _3DOB.threeMesh = sphereYup;
     _3DOB.minWGS84 = minWGS84;
     _3DOB.maxWGS84 = maxWGS84;
     _3Dobjects.push(_3DOB);
     geometry=new THREE.DodecahedronGeometry();
     let dodecahedronMesh=new THREE.Mesh(geometry,new THREE.MeshNormalMaterial());   //12面体
     dodecahedronMesh.scale.set(5000,5000,5000);
     dodecahedronMesh.position.z+=15000;
     // translate "up" in Three.js space so the "bottom" of the mesh is the handle
     dodecahedronMesh.rotation.x = Math.PI / 2; // rotate mesh for Cesium's Y-up system
     dodecahedronMesh.uuid="12面体";
     var dodecahedronMeshYup = new THREE.Group();
     dodecahedronMeshYup.add(dodecahedronMesh)
     three.scene.add(dodecahedronMeshYup); // don’t forget to add it to the Three.js scene manually
     dodecahedronMeshYup.position.set(ce.x,ce.y,ce.z);
     //    Assign Three.js object mesh to our object array
      _3DOB = new _3DObject();
      _3DOB.threeMesh = dodecahedronMeshYup;
      _3DOB.minWGS84 = minWGS84;
      _3DOB.maxWGS84 = maxWGS84;
      _3Dobjects.push(_3DOB);
     //添加灯光
     //添加点光源
     var spotLight = new THREE.SpotLight(0xffffff);
     spotLight.position.set(0,0,50000);
     spotLight.castShadow = true; //设置光源投射阴影
     spotLight.intensity = 1;
     sphereYup.add(spotLight)
     //添加环境光
     var hemiLight = new THREE.HemisphereLight(0xff0000, 0xff0000, 1);
     sphereYup.add(hemiLight);
     var 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 (id in _3Dobjects) {
         minWGS84 = _3Dobjects[id].minWGS84;
         maxWGS84 = _3Dobjects[id].maxWGS84;
         // convert lat/long center position to Cartesian3
         var center = Cesium.Cartesian3.fromDegrees((minWGS84[0] + maxWGS84[0]) / 2, (minWGS84[1] + maxWGS84[1]) / 2);

         // get forward direction for orienting model
         var 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
         var bottomLeft = cartToVec(Cesium.Cartesian3.fromDegrees(minWGS84[0], minWGS84[1]));
         var topLeft = cartToVec(Cesium.Cartesian3.fromDegrees(minWGS84[0], maxWGS84[1]));
         var latDir = new THREE.Vector3().subVectors(bottomLeft, topLeft).normalize();

         // configure entity position and orientation
         _3Dobjects[id].threeMesh.position.copy(center);
         _3Dobjects[id].threeMesh.lookAt(centerHigh);
         _3Dobjects[id].threeMesh.up.copy(latDir);
     }
 }

3、效果如下图

在这里插入图片描述 在这里插入图片描述

4、遇到的坑

1.要注意threejs的版本。文中所用为r87版的threejs,尝试换成r92r103,都找不到模型,这个问题后续再探究一下是为什么。 2.关于three的模型在cesium中的位置变幻: 文中设置了一个最大坐标和最小坐标,位置如图: 根据最大最小坐标绘制的polygon

位置变换前three的坐标系如图(红色为x轴,绿色为y轴,蓝色为z轴):

在这里插入图片描述 在这里插入图片描述 进行位置变化后: 在这里插入图片描述 4.关于自己的模型变黑:加载帖子中的两个自建模型,颜色是很明显的彩色,如图所示: 在这里插入图片描述 加载其他自建模型或其他导入的模型,则是如图: 在这里插入图片描述 原因是threejs渲染器中并没有加灯光进去,加上灯光就好了。

5、完整代码

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>three and cesium</title>
    <script src="./js/Cesium/Cesium.js"></script>
    <script src="./js/threer69.js"></script>
</head>

<body>
    <div id="cesiumContainer"></div>
    <div id="ThreeContainer"></div>

    <style>
        @import url(./js/Cesium/Widgets/widgets.css);

        body {
            height: 100%;
            width: 100%;
            margin: 0;
            overflow: hidden;
            padding: 0;
            background: #000;
        }

        #cesiumContainer {
            position: absolute;
            top: 0;
            left: 0;
            height: 100%;
            width: 100%;
            margin: 0;
            overflow: hidden;
            padding: 0;
            font-family: sans-serif;
        }

        #ThreeContainer {
            position: absolute;
            top: 0;
            left: 0;
            height: 100%;
            width: 100%;
            margin: 0;
            overflow: hidden;
            padding: 0;
            font-family: sans-serif;
            pointer-events: none;
        }

        .fullWindow {
            position: absolute;
            top: 0;
            left: 0;
            height: 100%;
            width: 100%;
            margin: 0;
            overflow: hidden;
            padding: 0;
            font-family: sans-serif;
        }

        .loadingIndicator {
            display: block;
            position: absolute;
            top: 50%;
            left: 50%;
            margin-top: -33px;
            margin-left: -33px;
            width: 66px;
            height: 66px;
            background-position: center;
            background-repeat: no-repeat;
            background-image: url(Images/ajax-loader.gif);
        }
    </style>
    <script>
        //cesium

        let minWGS84 = [115.23, 39.55];
        let maxWGS84 = [116.23, 41.55];
        let cesiumContainer = document.getElementById("cesiumContainer");
        let ThreeContainer = document.getElementById("ThreeContainer");
        let _3Dobjects = [];
        let three = {
            renderer: null,
            camera: null,
            scene: null
        };
        let cesium = {
            viewer: null
        }
        main();
        function main() {
            initCesium();
            initThree();
            init3DObject();
            loop();
        }
        function initCesium() {
            let esri = new Cesium.ArcGisMapServerImageryProvider({
                url: 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer'
            });
            var googlemap = new Cesium.UrlTemplateImageryProvider(
                {
                    url: 'http://mt1.google.cn/vt/lyrs=s&hl=zh-CN&x={x}&{x}&y={y}&z={z}&s=Gali'
                })
            cesium.viewer = new Cesium.Viewer("cesiumContainer", {
                useDefaultRenderLoop: false,
                selectionIndicator: false,
                homeButton: false,
                sceneModePicker: false,
                navigationHelpButton: false,
                animate: false,
                timeline: false,
                fullscreenButton: false,
                navigationInstructionsInitiallyVisible: false,
                allowTextureFilterAnisotropic: false,
                contextOptions: {
                    webgl: {
                        alpha: false,
                        antialias: true,
                        preserveDrawingBuffer: true,
                        failIfMajorPerformanceCaveat: false,
                        depth: true,
                        stencil: false,
                        anialias: false
                    }
                },
                targetFrameRate: 60,
                resolutionScale: 0.1,
                orderIndependentTranslucency: true,
                // creditContainer:"CreditDisplay",
                // imageeryProvider: googlemap,   //谷歌地图
                baseLayerPicker: true,
                geocoder: false,
                automaticallyTrackDataSourceClocks: false,
                dataSources: null,
                clock: null,
                terrainShadows: Cesium.ShadowMode.DISABLED
            });
            let center = Cesium.Cartesian3.fromDegrees(
                (minWGS84[0] + maxWGS84[0]) / 2,
                ((minWGS84[1] + maxWGS84[1]) / 2) - 1,
                200000
            );
            ce = center;
            cesium.viewer.camera.flyTo({
                destination: center,
                orientation: {
                    heading: Cesium.Math.toRadians(0),
                    pitch: Cesium.Math.toRadians(-60),
                    roll: Cesium.Math.toRadians(0)
                },
                duration: 3
            });
        }
        function initThree() {
            let fov = 45;
            let width = window.innerWidth;
            let height = window.innerHeight;
            let aspect = width / height;
            let near = 1;
            let far = 10 * 1000 * 1000; // needs to be far to support Cesium's world-scale rendering
            three.scene = new THREE.Scene();
            three.camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
            three.renderer = new THREE.WebGLRenderer({ alpha: true });
            // let axis=new THREE.AxesHelper(1000*1000*1000);
            // three.scene.add(axis);
            ThreeContainer.appendChild(three.renderer.domElement);
        }
        function init3DObject() {
            let entity = {
                name: 'Polygon',
                polygon: {
                    hierarchy: Cesium.Cartesian3.fromDegreesArray([
                        minWGS84[0], minWGS84[1],
                        maxWGS84[0], minWGS84[1],
                        maxWGS84[0], maxWGS84[1],
                        minWGS84[0], maxWGS84[1]
                    ]),
                    material: Cesium.Color.RED.withAlpha(0.1)
                }
            }
            let Polypon = cesium.viewer.entities.add(entity);
            let doubleSideMaterial = new THREE.MeshNormalMaterial({
                side: THREE.DoubleSide
            });

            geometry = new THREE.SphereGeometry(1, 32, 32);
            let sphere = new THREE.Mesh(geometry, new THREE.MeshPhongMaterial({ color: 0xffffff, side: THREE.DoubleSide }));   //12面体
            // sphere.scale.set(5000,5000,5000);
            // sphere.position.z+=15000;
            // translate "up" in Three.js space so the "bottom" of the mesh is the handle
            sphere.scale.set(5000, 5000, 5000);
            sphere.uuid = "sphere";
            var sphereYup = new THREE.Group();
            sphereYup.add(sphere)
            three.scene.add(sphereYup); // don’t forget to add it to the Three.js scene manually
            sphereYup.position.set(ce.x, ce.y, ce.z);
            _3DOB = new _3DObject();
            _3DOB.threeMesh = sphereYup;
            _3DOB.minWGS84 = minWGS84;
            _3DOB.maxWGS84 = maxWGS84;
            _3Dobjects.push(_3DOB);
            geometry = new THREE.DodecahedronGeometry();
            let dodecahedronMesh = new THREE.Mesh(geometry, new THREE.MeshNormalMaterial());   //12面体
            dodecahedronMesh.scale.set(5000, 5000, 5000);
            dodecahedronMesh.position.z += 15000;
            // translate "up" in Three.js space so the "bottom" of the mesh is the handle
            dodecahedronMesh.rotation.x = Math.PI / 2; // rotate mesh for Cesium's Y-up system
            dodecahedronMesh.uuid = "12面体";
            var dodecahedronMeshYup = new THREE.Group();
            dodecahedronMeshYup.add(dodecahedronMesh)
            three.scene.add(dodecahedronMeshYup); // don’t forget to add it to the Three.js scene manually
            dodecahedronMeshYup.position.set(ce.x, ce.y, ce.z);
            //    Assign Three.js object mesh to our object array
            _3DOB = new _3DObject();
            _3DOB.threeMesh = dodecahedronMeshYup;
            _3DOB.minWGS84 = minWGS84;
            _3DOB.maxWGS84 = maxWGS84;
            _3Dobjects.push(_3DOB);
            //添加灯光
            //添加点光源
            var spotLight = new THREE.SpotLight(0xffffff);
            spotLight.position.set(0, 0, 50000);
            spotLight.castShadow = true; //设置光源投射阴影
            spotLight.intensity = 1;
            sphereYup.add(spotLight)
            //添加环境光
            var hemiLight = new THREE.HemisphereLight(0xff0000, 0xff0000, 1);
            sphereYup.add(hemiLight);
            var 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 (id in _3Dobjects) {
                minWGS84 = _3Dobjects[id].minWGS84;
                maxWGS84 = _3Dobjects[id].maxWGS84;
                // convert lat/long center position to Cartesian3
                var center = Cesium.Cartesian3.fromDegrees((minWGS84[0] + maxWGS84[0]) / 2, (minWGS84[1] + maxWGS84[1]) / 2);

                // get forward direction for orienting model
                var 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
                var bottomLeft = cartToVec(Cesium.Cartesian3.fromDegrees(minWGS84[0], minWGS84[1]));
                var topLeft = cartToVec(Cesium.Cartesian3.fromDegrees(minWGS84[0], maxWGS84[1]));
                var latDir = new THREE.Vector3().subVectors(bottomLeft, topLeft).normalize();

                // configure entity position and orientation
                _3Dobjects[id].threeMesh.position.copy(center);
                _3Dobjects[id].threeMesh.lookAt(centerHigh);
                _3Dobjects[id].threeMesh.up.copy(latDir);
            }
        }
        function _3DObject() {
            this.threeMesh = null;
            this.minWGS84 = null;
            this.maxWGS84 = null;
        }

        function loop() {
            requestAnimationFrame(loop);
            renderCesium();
            renderThreeObj();
            renderCamera();
        }

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

        function renderThreeObj() {
            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.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.camera.lookAt(new THREE.Vector3(0, 0, 0));

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

    </script>
</body>

</html>

6、鲸讯社区