招商网站建设运营,大气集团企业网站源码,专注徐州网站开发,科技股龙头接上次的视频投影#xff0c;Leader告诉我这个视频投影要用在两个地方#xff0c;一个是我原先写的轨迹回放那里#xff0c;另一个在无人机起飞后的地图回显#xff0c;要实时播放无人机拍摄的视频#xff0c;还要能转镜头#xff0c;让我把这个也接一下。 我的天#x…接上次的视频投影Leader告诉我这个视频投影要用在两个地方一个是我原先写的轨迹回放那里另一个在无人机起飞后的地图回显要实时播放无人机拍摄的视频还要能转镜头让我把这个也接一下。 我的天告诉我的时候人都傻了这是一个功能嘛 一个是拿到了全部的轨迹数据进行回显播放的视频也是完整的资源视频要求投射在地面上。另一个是接收实时的轨迹数据进行回显播放的是实时的直播视频居然还要求跟着镜头一起转。 这是两个完完全全不一样的功能好吧 我拿着标书仔仔细细看过那行“演示无人机画面投影到地图上”一时间陷入了沉默。 无语归无语但特么还是要写跟Leader据理力争这不是改改就能换上的功能然后争取来两周的研发时间。 两周时间看起来很长其实也就10天有时候我写个计算方法都要两天还不一定是最终版所以只能拜托接下来运气很好别让我遇到太多阻碍。 开工吧
Step 0 思路最初都是想要拿原来的方法改改看哪怕不成功也绝了这个念想。 首先要解决的是视频投放的问题原本这个投影方法只能投放视频且只能投影到地面我要优化成能投影直播并且不接触地面也能投放。 为了解决直播投放问题我尝试修改了源码发现困难重重主要是两点一个是原方法不依赖dom元素这意味着我不能暴力篡改成直播通道另一个是封装层级过多我想在某几个关键步骤看下效果都不能被满足只能盲写看最终效果。 卡在第一步我是万万没有想到经过一下午的尝试最终让我放弃了继续下去的想法。
Step0.1当然我也没有急着完全放弃之前的代码我还记得我czml的无人机轨迹方法已经很完善了从静态路径加载改为动态路径回显似乎并不难如果成功的话只需要给视频材质连四根线接着解决朝向转动的问题就完成了大大减轻了工作量。 我将原有轨迹数据做成坐标发射器通过动态接收点位组成路径每次接收到数据就更新czml加载的点位集合成功改成了动态路径回显。 然后我尝试将我之前做好的视椎体视频替换进去然后切换朝向发现在dataSource.then中格外难操作矩阵需要多考虑很多问题于是暂时搁置该方法等待重启机会。
旧路已死新步骤开始从0开始一点点搭建功能。
Step 1: 思路 选择实体结合时间轴更为灵活地构建动画同时代码将更繁琐。 首先写一段简单动画从一个点到另一个点。
部分代码
var startPosition Cesium.Cartesian3.fromDegrees(-75.0, 40.0, 1000.0);
var endPosition Cesium.Cartesian3.fromDegrees(-75.0, 42.0, 1000.0);// 设置时间范围
var startTime Cesium.JulianDate.now();
var endTime Cesium.JulianDate.addSeconds(startTime,15,new Cesium.JulianDate()
);// 创建一个 SampledPositionProperty 来定义路径
var positionProperty new Cesium.SampledPositionProperty();// 添加时间和位置样本
positionProperty.addSample(startTime, startPosition);
positionProperty.addSample(endTime, endPosition);// 创建一个 entity 并设置其 position 为定义的路径
var entity viewer.entities.add({position: positionProperty,point: {pixelSize: 10,color: Cesium.Color.RED,},
});// 使视图跟踪 entity
viewer.trackedEntity entity;// 设置时钟范围
viewer.clock.startTime startTime.clone();
viewer.clock.stopTime endTime.clone();
viewer.clock.currentTime startTime.clone();
viewer.clock.clockRange Cesium.ClockRange.LOOP_STOP; // 当到达结束时间时停止
viewer.clock.multiplier 1; // 时间加速倍率
viewer.clock.clockRange Cesium.ClockRange.CLAMPED;
viewer.clock.shouldAnimate true;Step 2: 思路两个点变多个点写一个点位生成器模拟实时接收数据实现多点位连续飞行
Step 3: 思路模拟点位飞行点换成模型视椎体跟随模型一起并封装成class效果很好但视角连贯性有待提高 部分代码
class PointMover {constructor(viewer) {this.viewer viewer;this.pointQueue [];this.isAnimating false;this.videoEntity null;// 添加模型this.entity ...// 添加视椎体this.frustumPrimitive viewer.scene.primitives.add(new Cesium.Primitive({geometryInstances: new Cesium.GeometryInstance({geometry: geo,attributes: {color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.RED.withAlpha(0.5)),},}),appearance: new Cesium.PerInstanceColorAppearance({translucent: true,flat: true,}),asynchronous: false,}));// 初始化视频材质this.videoEntity viewer.entities.add({name: uav-tmp-fly-wxsimple,polygon: {hierarchy: new Cesium.CallbackProperty((time) {// 添加安全检查if (!this.entity || !this.entity.position) {return null;}try {const position this.entity.position.getValue(time);if (!Cesium.defined(position)) {return null;}// 计算视锥体的远截面四个角点// 返回新的多边形层次结构return new Cesium.PolygonHierarchy([upRightPt,upLeftPt,downLeftPt,downRightPt,]);} catch (error) {console.warn(Error calculating polygon positions:, error);return null;}}, false),perPositionHeight: true,material: videoElement,// 添加其他属性以提高渲染性能shadows: Cesium.ShadowMode.DISABLED,classificationType: Cesium.ClassificationType.BOTH,zIndex: 999,},});// 内部方法启动动画startAnimation() {if (this.pointQueue.length 2 !this.isAnimating) {// 在 onTick 事件处理函数中更新视锥体位置const onTick (clock) {// 移除旧的视锥体// 添加新的视锥体// ...}// 添加 onTick 事件监听器this.viewer.clock.onTick.addEventListener(onTick);}}// 外部调用方法更新点位并启动动画updatePositionsAndAnimate(newPosition) {this.pointQueue.push(newPosition);if (!this.isAnimating) {this.startAnimation();}}}
}
Step 4: 思路加入虚线路径 部分代码
class PointMover {constructor(viewer) {this.viewer viewer;this.pointQueue [];this.isAnimating false;this.videoEntity null;this.pathPoints []; // 用于记录路径点this.pathPolyline null; // 用于绘制路径// ...}// 绘制路径的方法createPathPolyline() {this.pathPolyline this.viewer.entities.add({polyline: {positions: new Cesium.CallbackProperty((time) {// 取当前记录的路径点return this.pathPoints;}, false),material: new Cesium.PolylineDashMaterialProperty({dashLength: 16.0, // 虚线的长度}),width: 3,},});}// 更新路径并添加新的位置updatePath(newPosition) {this.pathPoints.push(newPosition); // 将新位置加入到路径点// 更新虚线的路径if (this.pathPolyline) {this.pathPolyline.polyline.positions new Cesium.CallbackProperty((time) {return this.pathPoints;},false);}}// 内部方法启动动画startAnimation() {if (this.pointQueue.length 2 !this.isAnimating) {// 在 onTick 事件处理函数中更新视锥体位置const onTick (clock) {// 将当前位置添加到路径数组this.updatePath(currentPosition);//...}}}
}Step 4延伸: 思路在Step 4基础上尝试扩展显隐,销毁 show的配合 部分代码
class PointMover {constructor(viewer, show) {this.viewer viewer;this.pointQueue [];this.isAnimating false;this.videoEntity null;this.pathPoints [];this.pathPolyline null; this.isFrustumShow show; //用于显隐控制// ...if (this.frustumPrimitive) {if (!this.isFrustumShow) {this.frustumPrimitive.show false;} else {this.frustumPrimitive.show true;}}if (this.videoEntity) {if (!this.isFrustumShow) {this.videoEntity.show false;} else {this.videoEntity.show true;}}}// 内部方法启动动画startAnimation() {if (this.pointQueue.length 2 !this.isAnimating) {// 在 onTick 事件处理函数中更新视锥体位置const onTick (clock) {// 将当前位置添加到路径数组//...this.frustumPrimitive this.viewer.scene.primitives.add(new Cesium.Primitive({geometryInstances: new Cesium.GeometryInstance({geometry: newGeometry,attributes: {color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.RED.withAlpha(0.5)),},}),appearance: new Cesium.PerInstanceColorAppearance({translucent: true,flat: true,}),asynchronous: false,}));if (this.frustumPrimitive) {if (!this.isFrustumShow) {this.frustumPrimitive.show false;} else {this.frustumPrimitive.show true;}}if (this.videoEntity) {if (!this.isFrustumShow) {this.videoEntity.show false;} else {this.videoEntity.show true;}}}}}// ...// 外部调用方法更新显隐开关updateVisibleAndHidden(val) {this.isFrustumShow val;}dispose() {if (this.frustumPrimitive) {viewer.scene.primitives.remove(this.frustumPrimitive);this.frustumPrimitive null;}if (this.videoEntity) {viewer.entities.remove(this.videoEntity);this.videoEntity null;}if (this.entity) {viewer.entities.remove(this.entity);this.entity null;}if (this.pathPolyline) {viewer.entities.remove(this.pathPolyline);this.pathPolyline null;}}
}
if (this.pointMover) {clearInterval(this.intervalId);this.intervalId null;this.pointMover.dispose();this.pointMover null;
}
this.pointMover new PointMover(viewer, this.isFrustumShow);Step 5: 思路尝试改变视椎体朝向 部分代码
class PointMover {constructor(viewer) {// ...this.currentHeading 0;this.currentPitch 0;this.currentRoll 0;// ...}// 内部方法启动动画startAnimation() {if (this.pointQueue.length 2 !this.isAnimating) {// 在 onTick 事件处理函数中更新视锥体位置const onTick (clock) {const heading Cesium.Math.toRadians(this.currentHeading); // 偏航角const pitch Cesium.Math.toRadians(this.currentPitch); // 俯仰角const roll Cesium.Math.toRadians(this.currentRoll); // 翻滚角// 创建一个HeadingPitchRoll对象const headingPitchRoll new Cesium.HeadingPitchRoll(heading,pitch,roll);// 创建一个旋转矩阵const rotationMatrix Cesium.Transforms.headingPitchRollToFixedFrame(currentPosition,headingPitchRoll,Cesium.Ellipsoid.WGS84);// 从旋转矩阵计算四元数const orientation Cesium.Quaternion.fromRotationMatrix(rotationMatrix);//...}}}// ...updateHeadingPitchRoll(heading, patch, roll) {this.currentHeading heading;this.currentPitch patch;this.currentRoll roll;}
}Step 5延伸: 思路在Step 5基础上尝试同样矩阵转动视频材质 发现视频材质无法贴合视椎体方案终止
Step 6: 思路换用另一种视椎体构建方法采用相机视角重置视椎体视角的方法消除地理位置的影响 部分代码
class PointMover {constructor(viewer) {// ...this.videoEntity viewer.entities.add({name: uav-tmp-fly-wxsimple,polygon: {hierarchy: new Cesium.CallbackProperty((time) {if (!this.entity || !this.entity.position) {return null;}try {const position this.entity.position.getValue(time);if (!Cesium.defined(position)) {return null;}let frustum this.camera.frustum;let Cartesian3 Cesium.Cartesian3;let camera this.camera;// ...return new Cesium.PolygonHierarchy([upRightPt,upLeftPt,downLeftPt,downRightPt,]);} catch (error) {console.warn(Error calculating polygon positions:, error);return null;}}, false),perPositionHeight: true,material: new Cesium.ImageMaterialProperty({image: videoElement, // 这里传入视频元素transparent: true, // 设置透明repeat: new Cesium.Cartesian2(1.0, 1.0), // 控制重复}),shadows: Cesium.ShadowMode.DISABLED,classificationType: Cesium.ClassificationType.BOTH,zIndex: 999,},});}// 内部方法启动动画startAnimation() {if (this.pointQueue.length 2 !this.isAnimating) {// 在 onTick 事件处理函数中更新视锥体位置const onTick (clock) {var scene this.viewer.scene; this.camera new Cesium.Camera(scene)//...}}}// ...
}最终 接入实际项目无人机航拍镜头实时同步反显完结撒花°˖✧◝(⁰▿⁰)◜✧˖°