cesium之平面裁切

初学者,原理可能没这么深入,不过基本了解实现的步骤的逻辑

基本的原理就是切面发法线方向为显示的部分,法线的反方向为裁切掉的部分

动态的改变distance来实现模型的裁切

 

实现主要用到的api有,ClippingPlaneCollection,ClippingPlane

以下奉上具体实现步骤

1.定义裁切平面数组

 var planesDistance = [];//用于存放多个裁切面距离模型原点的距离
 var clippingPlanesArray = [
                new Cesium.ClippingPlane(new Cesium.Cartesian3(0.0, 0.0, -1.0), 0.0),//往下为裁切
                 new Cesium.ClippingPlane(new Cesium.Cartesian3(-1.0, 0.0, 0.0), 0.0),//侧面裁切
                
            ];
2.定义裁切平面集合
 //debugger;
            //定义裁切平面集合                      
            clippingPlanes = new Cesium.ClippingPlaneCollection({
                planes: clippingPlanesArray,//定义的切面数组//变成undid
                edgeColor: Cesium.Color.WHITE, // 平面切割时模型的边缘颜色
                edgeWidth: viewModel.edgeStylingEnabled ? 1.0 : 0.0,//边缘的宽度y 
                unionClippingRegions: true, //true 才能多个切割  
                enabled: true,//裁切平面是否处于活动
               clippingPlanes
            });
3.加载模型并制定裁切平面
 var tileset = viewer.scene.primitives.add(new Cesium.Cesium3DTileset({
                    url: url,
                    luminanceAtZenith: 0.2,
                    lightColor: new Cesium.Cartesian3(0.3, 0.3, 0.3),
                   clippingPlanes:clippingPlanes
                }));
4.创建可视化裁切平面           
            tileset.debugShowBoundingVolume = viewModel.debugBoundingVolumesEnabled;
            boundingSphere = tileset.boundingSphere;//3d模型的中心和半径
            var radius = boundingSphere.radius;//
            viewer.zoomTo(tileset, new Cesium.HeadingPitchRange(0.5, -0.2, radius * 4.0));
            //加载plane可视化裁切平面
            if (!Cesium.Matrix4.equals(tileset.root.transform, Cesium.Matrix4.IDENTITY)) {
                // The clipping plane is initially positioned at the tileset's root transform.
                // Apply an additional matrix to center the clipping plane on the bounding sphere center.
                var transformCenter = Cesium.Matrix4.getTranslation(tileset.root.transform, new Cesium.Cartesian3());//获取矩阵的平移部分
                var transformCartographic = Cesium.Cartographic.fromCartesian(transformCenter);//结果对象中的值将以弧度表示。
                var boundingSphereCartographic = Cesium.Cartographic.fromCartesian(tileset.boundingSphere.center);
                var boundingSphereCartographicradius = Cesium.Cartographic.fromCartesian(radius);
                var height = boundingSphereCartographic.height - transformCartographic.height;
                console.log(boundingSphereCartographicradius);
                var longitude = boundingSphereCartographic.longitude - transformCartographic.longitude;//经度
                console.log(longitude * 111000);
                var latitude = boundingSphereCartographic.latitude - transformCartographic.latitude;//维度
                //下面决定了是否在中心点
                clippingPlanes.modelMatrix = Cesium.Matrix4.fromTranslation(new Cesium.Cartesian3(40, 50, height));

            }
            //创建可视化对象
            for (var i = 0; i < clippingPlanes.length; ++i) {
                var plane = clippingPlanes.get(i);
                viewer.entities.add({
                    name: i,
                    position: boundingSphere.center,//设置裁切面的位置,offset, 根据3dtiles同步调整裁切面高度 
                    //PlaneGraphics.html
                    plane: {//每个裁切对象(distance为裁切面距离远点的高度)
                        dimensions: new Cesium.Cartesian2(radius, radius),//调整裁切面的长和宽
                        material: Cesium.Color.WHITE.withAlpha(0.1),//裁切面的颜色和透明度
                        plane: new Cesium.CallbackProperty(createPlaneUpdateFunction(plane, radius, i), false),
                        outline: true,//是否显示边框
                        outlineColor: Cesium.Color.WHITE,//边框颜色
                    }
                });//add
            }
        }
5.写一个方法动态改变ClippingPlane距离原点的距离
        function createPlaneUpdateFunction(plane, radius, name) {
            return function () {
                plane.distance = planesDistance[name] + (radius * 0.5);//切面距离原点的距离就等于移动的距离+模型的半径的一半
                return plane;
            };
        }
 
6.定义鼠标事件
 var ellipsoid = viewer.scene.globe.ellipsoid;
            // 注册鼠标事件
            var downHandler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);//鼠标点击事件
            downHandler.setInputAction(function (movement) {
                var position = viewer.scene.camera.pickEllipsoid(movement.position, viewer.scene.globe.ellipsoid);
                //console.log(position);
                var pickObject = scene.pick(movement.position);
                //debugger;
                if (Cesium.defined(pickObject) && Cesium.defined(pickObject.id) && Cesium.defined(pickObject.id.plane)) {
                    selectedPlane = pickObject.id.plane;//将entities
                    selectedPlane.name = pickObject.id.name//将name赋予selectedPlane
                    selectedPlane.material = Cesium.Color.WHITE.withAlpha(0.05);
                    selectedPlane.outlineColor = Cesium.Color.WHITE;//更换切面的颜色
                    selectedPlane.startPosition = movement.position;//
                    selectedPlane.startPosition1 = Cesium.Math.toDegrees((ellipsoid.cartesianToCartographic(position).longitude));//初始位置维度
                    scene.screenSpaceCameraController.enableInputs = false; // 取消默认的鼠标一切输入事件
                }
            }, Cesium.ScreenSpaceEventType.LEFT_DOWN);


            // 注册鼠标松开事件
            var upHandler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);//鼠标点击向上事件
            upHandler.setInputAction(function () {

                if (Cesium.defined(selectedPlane)) {//如果存在这个对象
                    selectedPlane.material = Cesium.Color.WHITE.withAlpha(0.6); // 恢复选中的切面颜色
                    selectedPlane.outlineColor = Cesium.Color.blue;//
                    selectedPlane = undefined;
                }
                scene.screenSpaceCameraController.enableInputs = true; // 恢复默认的鼠标一切输入事件
            }, Cesium.ScreenSpaceEventType.LEFT_UP);

            // 注册鼠标移动事件,
            var moveHandler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);//鼠标点击移动事件
            moveHandler.setInputAction(function (movement) {
                //通过指定的椭球或者地图对应的坐标系,将鼠标的二维坐标转换为对应椭球体三维坐标
                var cartesianEnd = viewer.camera.pickEllipsoid(movement.endPosition, ellipsoid);//笛卡尔积,获取鼠标移动结束世界坐标
                var cartesianStart = viewer.camera.pickEllipsoid(movement.startPosition, ellipsoid);//笛卡尔积,获取鼠标开始移动时世界坐标
                if (Cesium.defined(selectedPlane)) {
                   
                  //  console.log(selectedPlane.name);//做判断哪个面
                    if (selectedPlane.name == 0) {//往下
                        var deltaSize = movement.startPosition.y - movement.endPosition.y; // 计算鼠标移动的过程中产生的垂直高度距离
                        console.log("开始")
                        console.log("起始位置"+movement.startPosition.y);
                        console.log("结束为止"+movement.endPosition.y);
                        console.log("移动的幅度"+deltaSize);
                        planesDistance[selectedPlane.name] += deltaSize ;//更改鼠标移动的幅度//从初始位置开始算
                        console.log("结束")
                        
                    }

                    if (selectedPlane.name == 1) {//侧面
                        if (cartesianEnd&&cartesianStart) {//能获取
                            //将笛卡尔坐标转换为地理(地图)坐标
                            var cartographiEnd = ellipsoid.cartesianToCartographic(cartesianEnd);
                            var cartographicStart = ellipsoid.cartesianToCartographic(cartesianStart);
                            //console.log(cartographic);
                            //将弧度转为度的十进制度表示,因为差值很小,保留20位小数
                           var  longitudeStringEnd = (Cesium.Math.toDegrees(cartographiEnd.longitude).toFixed(20));/
                           var  longitudeStringStart = (Cesium.Math.toDegrees(cartographicStart.longitude).toFixed(20));/
                           var longitudeGap=(longitudeStringEnd-longitudeStringStart)*100000;
            
                           console.log("两者之间的差距"+longitudeGap);
                           console.log("++===");/获取经度往上变大
                            /
                            planesDistance[selectedPlane.name] +=longitudeGap;//原来的距离加上移动的距离(很重要,决定了裁切面的移动方向)
                        } else {
                            // mouse_state.innerText = "";
                        }

                    }
                    

                }
            }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
 
 
总结:侧面的裁切面不能以屏幕坐标作为移动的依据,因为当旋转模型时,正负值会发生改变,切面移动方向会和鼠标移动的方向相反,
然后我个人的解决方案是获取地球坐标,然后再转换成经纬,再以鼠标移动时经纬度差作为侧面切面移动的依据,这样就可以解决以上的问题,如果大家有更好的解决方案欢迎跟我交流
 
还有个问题,就是我侧面的裁切面还没触碰模型就开始裁切,需要我手动去调,现在还没解决,知道解决方案的大神也欢迎给我指点
 
posted @ 2020-05-05 21:24  努力啊少年  阅读(3607)  评论(1编辑  收藏  举报