第一人称漫游

310 阅读1分钟

在浏览室内场景,视角飞行等场景中,除了第三人称的视角(相机视角)以外,还有一种视角就是第一人称视角,也就是室内模型漫游,或者以某个模型为视角切换基准,带来沉浸式体验。常用于模型室内漫游,景点漫游,管线漫游,河道漫游等等,关键点在于手动切换相机三元素。

具体实现如下:

1,初始化相机,设定相机位置(样本点)

2,控制相机运动

3,设置飞行的时间

4,相机原地定点转向

5,计算两点间的偏航角(弧度和角度转换)

具体如下

<!DOCTYPE html><html lang="en"><head>    <!-- Use correct character set. -->    <meta charset="utf-8"/>    <!-- Tell IE to use the latest, best version. -->    <meta http-equiv="X-UA-Compatible" content="IE=edge"/>    <!-- Make the application on mobile take up the full browser screen and disable user scaling. -->    <meta name="viewport"          content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"/>    <title>模型室内漫游</title>    <script src="https://data.sunbt.ltd/lib/Cesium-1.89/Build/Cesium/Cesium.js"></script>    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>    <style>        @import url(https://data.sunbt.ltd/lib/Cesium-1.89/Build/Cesium/Widgets/widgets.css);        html,        body, #temp {            width: 100%;            height: 100%;            margin: 0;            padding: 0;            overflow: hidden;        }        #cesiumContainer {            width: 100%;            height: 100%;            margin: 0;            padding: 0;            overflow: hidden;        }    </style></head><body><div id="temp">    <div style="display: -webkit-flex;display: flex;width: 100%;height: 100%">        <div style="width: 90%;height: 100%">            <div id="cesiumContainer"></div>        </div>        <div style="width: 10%;height: 100%;background-color: #d3d3d3;padding: 30px">            <button class="btn" @click="modelLocate">模型定位</button>            <button class="btn" @click="roamIndoor">模型漫游</button>            <button class="btn" @click="cancelFlyEvent">停止漫游</button>        </div>    </div></div><script>    let EarthComp = new Vue({        el: "#temp",        data: {            _earth: undefined, // 注意:Earth和Cesium的相关变量放在vue中,必须使用下划线作为前缀!            _viewer: undefined,            model: null,//切片模型            marks: [],            marksIndex: 1,            pitchValue: -10,            remainTime: 0,            usedTime: 0,        },        mounted: function () {            let that = this;            this.earthInit();        },        methods: {            /**             * 地球初始化             */            earthInit() {                                //初始页面加载                //Cesium.Ion.defaultAccessToken = cesium_tk;                let viewer = new Cesium.Viewer('cesiumContainer', {                    geocoder: false,   // 位置查找工具                    baseLayerPicker: false,// 图层选择器(地形影像服务)                    timeline: false, // 底部时间线                    homeButton: false,// 视角返回初始位置                    fullscreenButton: false, // 全屏                    animation: false,   // 左下角仪表盘(动画器件)                    sceneModePicker: false,// 选择视角的模式(球体、平铺、斜视平铺)                    navigationHelpButton: false, //导航帮助按钮                    imageryProvider: new Cesium.UrlTemplateImageryProvider({                        url:'https://tiles.geovis.online/base/v1/img/{z}/{x}/{y}?format=webp&token=c12979f9caccfc2e24b822852976c264c3c33403a87bafd21b0f4eb2de4bd79e',                        maximumLevel: 18,                    })                });                // //调用影响中文注记服务                // viewer.imageryLayers.addImageryProvider(new Cesium.WebMapTileServiceImageryProvider({                //     url: TDT_CIA_C,                //     layer: "tdtImg_c",                //     style: "default",                //     format: "tiles",                //     tileMatrixSetID: "c",                //     subdomains: ["t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7"],                //     tilingScheme: new Cesium.GeographicTilingScheme(),                //     tileMatrixLabels: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19"],                //     maximumLevel: 50,                //     show: false                // }))                this._viewer = viewer;                // 去除版权信息                this._viewer._cesiumWidget._creditContainer.style.display = "none";                // 初始化模型位置                this.addModel();            },            /**             * 添加模型             */            addModel() {                // 3Dtiles切片地址                let tileset = this._viewer.scene.primitives.add(new Cesium.Cesium3DTileset({                    url: 'http://192.168.1.197:8080/data/3dtiles/tileset.json',                    modelMatrix: Cesium.Matrix4.fromArray(                        [0.9972458032561666, 0.04372029028528979, 0.05991113506964879, 0,                            -0.03623787897545647, 0.9920229449104262, -0.12073646051879428, 0,                            -0.06471185374661931, 0.11823287609043515, 0.9908750491338749, 0,                            -663.0794944260269, 1211.490494620055, 2974.1003134818748, 1]),                }));                this.model = tileset;                this._viewer.flyTo(tileset, {                    offset: {                        heading: Cesium.Math.toRadians(20.0),//方向                        pitch: Cesium.Math.toRadians(-25),//倾斜角度                        range: 1000                    }                });            },            /**             * 模型定位             */            modelLocate() {                let that = this;                this._viewer.flyTo(this.model, {                    offset: {                        heading: Cesium.Math.toRadians(120.0),//方向                        pitch: Cesium.Math.toRadians(-10),//倾斜角度                        range: 450                    }                });            },            /**             * 室内漫游             * 初始化相机位置             */            roamIndoor() {                let that = this;                /** 相机视角飞行 开始 **/                this.marks = [                    // height:相机高度(单位米) flytime:相机两个标注点飞行时间(单位秒)                    {lng: 118.69184532414744, lat: 32.16119279415843, height: 5, flytime: 5},                    {lng: 118.69192079776337, lat: 32.16112689859642, height: 5, flytime: 5},                    {lng: 118.69223881961578, lat: 32.16098664838045, height: 5, flytime: 5}                ];// 地标集合 根据地标顺序来进行漫游                this.marksIndex = 1;                this.pitchValue = -10;                this._viewer.scene.camera.flyTo({                    //定位坐标点,建议使用谷歌地球坐标位置无偏差                    destination: Cesium.Cartesian3.fromDegrees(that.marks[0].lng, that.marks[0].lat, that.marks[0].height),                    duration: 2,   //定位的时间间隔                    orientation: {                        heading: Cesium.Math.toRadians(120.0),//方向                        pitch: Cesium.Math.toRadians(-10),//倾斜角度                        roll: 0                    }                });                setTimeout(function () {                    that.flyExtent();                }, 2000);            },            //控制相机运动方法(范围)            flyExtent() {                let that = this;                let marks = that.marks;                // let marksIndex = that.marksIndex;                let pitchValue = that.pitchValue;                let viewer = that._viewer;                // 相机看点的角度,如果大于0那么则是从地底往上看,所以要为负值                let pitch = Cesium.Math.toRadians(pitchValue);                // 时间间隔2秒钟                that.setExtentTime(that.marks[that.marksIndex].flytime);                let Exection = function TimeExecution() {                    let preIndex = that.marksIndex - 1;                    //当到达最后一个点时,继续漫游                    if (that.marksIndex === 0) {                        preIndex = marks.length - 1;                    }                    //计算偏航角                    let heading = that.bearing(marks[preIndex].lat, marks[preIndex].lng, marks[that.marksIndex].lat, marks[that.marksIndex].lng);                    heading = Cesium.Math.toRadians(heading);                    // 当前已经过去的时间,单位s                    let delTime = Cesium.JulianDate.secondsDifference(viewer.clock.currentTime, viewer.clock.startTime);                    let originLat = that.marksIndex === 0 ? marks[marks.length - 1].lat : marks[that.marksIndex - 1].lat;                    let originLng = that.marksIndex === 0 ? marks[marks.length - 1].lng : marks[that.marksIndex - 1].lng;                    //计算相机下一次的位置                    let endPosition = Cesium.Cartesian3.fromDegrees(                        (originLng + (marks[that.marksIndex].lng - originLng) / marks[that.marksIndex].flytime * delTime),                        (originLat + (marks[that.marksIndex].lat - originLat) / marks[that.marksIndex].flytime * delTime),                        marks[that.marksIndex].height                    );                    viewer.scene.camera.setView({                        destination: endPosition,                        orientation: {                            heading: heading,                            pitch: pitch,                        }                    });                    //当到达下一个点的时候,重新设置相机偏航角                    if (Cesium.JulianDate.compare(viewer.clock.currentTime, viewer.clock.stopTime) >= 0) {                        viewer.clock.onTick.removeEventListener(Exection);                        that.changeCameraHeading();                    }                };                //让cesium的时钟方法来监听该方法                viewer.clock.onTick.addEventListener(Exection);            },            // 设置飞行的时间到viewer的时钟里            setExtentTime(time) {                let that = this;                let viewer = that._viewer;                let startTime = Cesium.JulianDate.fromDate(new Date());                let stopTime = Cesium.JulianDate.addSeconds(startTime, time, new Cesium.JulianDate());                viewer.clock.startTime = startTime.clone();  // 开始时间                viewer.clock.stopTime = stopTime.clone();     // 结速时间                viewer.clock.currentTime = startTime.clone(); // 当前时间                viewer.clock.clockRange = Cesium.ClockRange.CLAMPED; // 行为方式                viewer.clock.clockStep = Cesium.ClockStep.SYSTEM_CLOCK; // 时钟设置为当前系统时间; 忽略所有其他设置。            },            // 相机原地定点转向            changeCameraHeading() {                let that = this;                let marks = that.marks;                let marksIndex = that.marksIndex;                let pitchValue = that.pitchValue;                let viewer = that._viewer;                let nextIndex = that.marksIndex + 1;                if (that.marksIndex === marks.length - 1) {                    nextIndex = 0;                }                // 计算两点之间的方向                let heading = that.bearing(marks[marksIndex].lat, marks[marksIndex].lng, marks[nextIndex].lat, marks[nextIndex].lng);                // 相机看点的角度,如果大于0那么则是从地底往上看,所以要为负值                let pitch = Cesium.Math.toRadians(pitchValue);                // 给定飞行一周所需时间,比如10s, 那么每秒转动度数                let angle = (heading - Cesium.Math.toDegrees(viewer.camera.heading)) / 2;                if (angle < -90)                    angle += 180;                else if (angle > 90)                    angle -= 180;                // 时间间隔2秒钟                that.setExtentTime(2);                // 相机的当前heading                let initialHeading = viewer.camera.heading;                let exection = function TimeExecution() {                    // 当前已经过去的时间,单位s                    let delTime = Cesium.JulianDate.secondsDifference(viewer.clock.currentTime, viewer.clock.startTime);                    let heading = Cesium.Math.toRadians(delTime * angle) + initialHeading;                    viewer.scene.camera.setView({                        orientation: {                            heading: heading,                            pitch: pitch,                        }                    });                    if (Cesium.JulianDate.compare(viewer.clock.currentTime, viewer.clock.stopTime) >= 0) {                        viewer.clock.onTick.removeEventListener(exection);                        that.marksIndex = ++that.marksIndex >= marks.length ? 0 : that.marksIndex;                        that.flyExtent();                    }                };                //让cesium的时钟方法来监听该方法                viewer.clock.onTick.addEventListener(exection);            },            //计算两点间的偏航角            bearing(startLat, startLng, destLat, destLng) {                startLat = this.toRadians(startLat);                startLng = this.toRadians(startLng);                destLat = this.toRadians(destLat);                destLng = this.toRadians(destLng);                let y = Math.sin(destLng - startLng) * Math.cos(destLat);                let x = Math.cos(startLat) * Math.sin(destLat) - Math.sin(startLat) * Math.cos(destLat) * Math.cos(destLng - startLng);                let brng = Math.atan2(y, x);                let brngDgr = this.toDegrees(brng);                return (brngDgr + 360) % 360;            },            /** 飞行时 camera的方向调整(heading) 开始 **/            // Converts from degrees to radians.            toRadians(degrees) {                return degrees * Math.PI / 180;            },            // Converts from radians to degrees.            toDegrees(radians) {                return radians * 180 / Math.PI;            },            /** 停止漫游方法 **/            cancelFlyEvent(eventType) {                let that = this;                //获取被clock监听的全部事件数量                let len = that._viewer.clock.onTick.numberOfListeners;                for (let i = 0; i < len; i++) {                    //将被监听的方法移除来停止方法                    that._viewer.clock.onTick.removeEventListener(that._viewer.clock.onTick._listeners[i]);                }            },        },    })</script></body></html>