> 技术文档 > cesium视频投影_cesium 视频投影

cesium视频投影_cesium 视频投影

先看效果视频投影效果图
使用cesium做视频投影效果,而且还要跟随无人机移动而移动,我现在用定时器更新无人机的坐标来实现效果具体代码如下:
1、CesiumVideo3d.js(某个cesium技术群大佬分享的)

// import ECEF from \"./CoordinateTranslate\";let CesiumVideo3d = (function () { var videoShed3dShader = \"\\r\\n\\r\\n\\r\\n\\r\\nuniform float mixNum;\\r\\nuniform sampler2D colorTexture;\\r\\nuniform sampler2D stcshadow; \\r\\nuniform sampler2D videoTexture;\\r\\nuniform sampler2D depthTexture;\\r\\nuniform mat4 _shadowMap_matrix; \\r\\nuniform vec4 shadowMap_lightPositionEC; \\r\\nuniform vec4 shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness; \\r\\nuniform vec4 shadowMap_texelSizeDepthBiasAndNormalShadingSmooth; \\r\\nvarying vec2 v_textureCoordinates;\\r\\nvec4 toEye(in vec2 uv, in float depth){\\r\\n vec2 xy = vec2((uv.x * 2.0 - 1.0),(uv.y * 2.0 - 1.0));\\r\\n vec4 posInCamera =czm_inverseProjection * vec4(xy, depth, 1.0);\\r\\n posInCamera =posInCamera / posInCamera.w;\\r\\n return posInCamera;\\r\\n}\\r\\nfloat getDepth(in vec4 depth){\\r\\n float z_window = czm_unpackDepth(depth);\\r\\n z_window = czm_reverseLogDepth(z_window);\\r\\n float n_range = czm_depthRange.near;\\r\\n float f_range = czm_depthRange.far;\\r\\n return (2.0 * z_window - n_range - f_range) / (f_range - n_range);\\r\\n}\\r\\nfloat _czm_sampleShadowMap(sampler2D shadowMap, vec2 uv){\\r\\n return texture2D(shadowMap, uv).r;\\r\\n}\\r\\nfloat _czm_shadowDepthCompare(sampler2D shadowMap, vec2 uv, float depth){\\r\\n return step(depth, _czm_sampleShadowMap(shadowMap, uv));\\r\\n}\\r\\nfloat _czm_shadowVisibility(sampler2D shadowMap, czm_shadowParameters shadowParameters){\\r\\n float depthBias = shadowParameters.depthBias;\\r\\n float depth = shadowParameters.depth;\\r\\n float nDotL = shadowParameters.nDotL;\\r\\n float normalShadingSmooth = shadowParameters.normalShadingSmooth;\\r\\n float darkness = shadowParameters.darkness;\\r\\n vec2 uv = shadowParameters.texCoords;\\r\\n depth -= depthBias;\\r\\n vec2 texelStepSize = shadowParameters.texelStepSize;\\r\\n float radius = 1.0;\\r\\n float dx0 = -texelStepSize.x * radius;\\r\\n float dy0 = -texelStepSize.y * radius;\\r\\n float dx1 = texelStepSize.x * radius;\\r\\n float dy1 = texelStepSize.y * radius;\\r\\n float visibility = \\r\\n (\\r\\n _czm_shadowDepthCompare(shadowMap, uv, depth)\\r\\n +_czm_shadowDepthCompare(shadowMap, uv + vec2(dx0, dy0), depth) +\\r\\n _czm_shadowDepthCompare(shadowMap, uv + vec2(0.0, dy0), depth) +\\r\\n _czm_shadowDepthCompare(shadowMap, uv + vec2(dx1, dy0), depth) +\\r\\n _czm_shadowDepthCompare(shadowMap, uv + vec2(dx0, 0.0), depth) +\\r\\n _czm_shadowDepthCompare(shadowMap, uv + vec2(dx1, 0.0), depth) +\\r\\n _czm_shadowDepthCompare(shadowMap, uv + vec2(dx0, dy1), depth) +\\r\\n _czm_shadowDepthCompare(shadowMap, uv + vec2(0.0, dy1), depth) +\\r\\n _czm_shadowDepthCompare(shadowMap, uv + vec2(dx1, dy1), depth)\\r\\n ) * (1.0 / 9.0)\\r\\n ;\\r\\n return visibility;\\r\\n}\\r\\nvec3 pointProjectOnPlane(in vec3 planeNormal, in vec3 planeOrigin, in vec3 point){\\r\\n vec3 v01 = point -planeOrigin;\\r\\n float d = dot(planeNormal, v01) ;\\r\\n return (point - planeNormal * d);\\r\\n}\\r\\nfloat ptm(vec3 pt){\\r\\n return sqrt(pt.x*pt.x + pt.y*pt.y + pt.z*pt.z);\\r\\n}\\r\\nvoid main() \\r\\n{ \\r\\n const float PI = 3.141592653589793;\\r\\n vec4 color = texture2D(colorTexture, v_textureCoordinates);\\r\\n vec4 currD = texture2D(depthTexture, v_textureCoordinates);\\r\\n if(currD.r>=1.0){\\r\\n gl_FragColor = color;\\r\\n return;\\r\\n }\\r\\n \\r\\n float depth = getDepth(currD);\\r\\n vec4 positionEC = toEye(v_textureCoordinates, depth);\\r\\n vec3 normalEC = vec3(1.0);\\r\\n czm_shadowParameters shadowParameters; \\r\\n shadowParameters.texelStepSize = shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.xy; \\r\\n shadowParameters.depthBias = shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.z; \\r\\n shadowParameters.normalShadingSmooth = shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.w; \\r\\n shadowParameters.darkness = shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness.w; \\r\\n shadowParameters.depthBias *= max(depth * 0.01, 1.0); \\r\\n vec3 directionEC = normalize(positionEC.xyz - shadowMap_lightPositionEC.xyz); \\r\\n float nDotL = clamp(dot(normalEC, -directionEC), 0.0, 1.0); \\r\\n vec4 shadowPosition = _shadowMap_matrix * positionEC; \\r\\n shadowPosition /= shadowPosition.w; \\r\\n if (any(lessThan(shadowPosition.xyz, vec3(0.0))) || any(greaterThan(shadowPosition.xyz, vec3(1.0)))) \\r\\n { \\r\\n gl_FragColor = color;\\r\\n return;\\r\\n }\\r\\n\\r\\n shadowParameters.texCoords = shadowPosition.xy; \\r\\n shadowParameters.depth = shadowPosition.z; \\r\\n shadowParameters.nDotL = nDotL; \\r\\n float visibility = _czm_shadowVisibility(stcshadow, shadowParameters); \\r\\n\\r\\n vec4 videoColor = texture2D(videoTexture,shadowPosition.xy);\\r\\n if(visibility==1.0){\\r\\n gl_FragColor = mix(color,vec4(videoColor.xyz,1.0),mixNum*videoColor.a);\\r\\n }else{\\r\\n if(abs(shadowPosition.z-0.0)<0.01){\\r\\n return;\\r\\n }\\r\\n gl_FragColor = color;\\r\\n }\\r\\n} \"; var Cesium=null var videoShed3d=function(cesium,viewer, param) { Cesium=cesium this.ECEF = new ECEF(); this.param = param; var option = this._initCameraParam(); this.optionType = { Color: 1, Image: 2, Video: 3 } this.near = option.near ? option.near : 0.1; if (option || (option = {}), this.viewer = viewer, this._cameraPosition = option.cameraPosition, this._position = option.position, this.type = option.type, this._alpha = option.alpha || 1, this.url = option.url, this.color = option.color, this._debugFrustum = Cesium.defaultValue(option.debugFrustum, !0), this._aspectRatio = option.aspectRatio || this._getWinWidHei(), this._camerafov = option.fov || Cesium.Math.toDegrees(this.viewer.scene.camera.frustum.fov), this.texture = option.texture || new Cesium.Texture({ context: this.viewer.scene.context, source: {  width: 1,  height: 1,  arrayBufferView: new Uint8Array([255, 255, 255, 255]) }, flipY: !1 }), this._videoPlay = Cesium.defaultValue(option.videoPlay, !0), this.defaultShow = Cesium.defaultValue(option.show, !0), !this.cameraPosition || !this.position) return void console.log(\'初始化失败:请确认相机位置与视点位置正确!\'); switch (this.type) { default: case this.optionType.Video: this.activeVideo(this.url); break; case this.optionType.Image: this.activePicture(this.url); this.deActiveVideo(); break; case this.optionType.Color: this.activeColor(this.color),  this.deActiveVideo(); } this._createShadowMap(), this._getOrientation(), this._addCameraFrustum() this._addPostProcess() this.viewer.scene.primitives.add(this) } Object.defineProperties(videoShed3d.prototype, { alpha: { get: function () { return this._alpha }, set: function (e) { return this._alpha = e } }, aspectRatio: { get: function () { return this._aspectRatio }, set: function (e) { this._aspectRatio = e,  this._changeVideoWidHei() } }, debugFrustum: { get: function () { return this._debugFrustum }, set: function (e) { this._debugFrustum = e,  this.cameraFrustum.show = e } }, fov: { get: function () { return this._camerafov }, set: function (e) { this._camerafov = e,  this._changeCameraFov() } }, cameraPosition: { get: function () { return this._cameraPosition }, set: function (e) { e && (this._cameraPosition = e, this._changeCameraPos()) } }, position: { get: function () { return this._position }, set: function (e) { e && (this._position = e, this._changeViewPos()) } }, videoPlay: { get: function () { return this._videoPlay }, set: function (e) { this._videoPlay = Boolean(e),  this._videoEle && (this.videoPlay ? this._videoEle.paly() : this._videoEle.pause()) } }, params: { get: function () { var t = {} return t.type = this.type,  this.type == this.optionType.Color ? t.color = this.color : t.url = this.url,  t.position = this.position,  t.cameraPosition = this.cameraPosition,  t.fov = this.fov,  t.aspectRatio = this.aspectRatio,  t.alpha = this.alpha,  t.debugFrustum = this.debugFrustum,  t } }, show: { get: function () { return this.defaultShow }, set: function (e) { this.defaultShow = Boolean(e),  this._switchShow() } } }) videoShed3d.prototype._initCameraParam = function () { var viewPoint = this.ECEF.enu_to_ecef({ longitude: this.param.position.x * 1, latitude: this.param.position.y * 1, altitude: this.param.position.z * 1 }, { distance: this.param.far, azimuth: this.param.rotation.y * 1, elevation: this.param.rotation.x * 1 }); var position = Cesium.Cartesian3.fromDegrees(viewPoint.longitude, viewPoint.latitude, viewPoint.altitude); var cameraPosition = Cesium.Cartesian3.fromDegrees(this.param.position.x * 1, this.param.position.y * 1, this.param.position.z * 1); return { type: 3, url: this.param.url, cameraPosition: cameraPosition, position: position, alpha: this.param.alpha, near: this.param.near, fov: this.param.fov, debugFrustum: this.param.debugFrustum } } /** * 旋转 */ videoShed3d.prototype._changeRotation = function (e) { if (e) { this.param.rotation = e; var option = this._initCameraParam(); this.position = option.position; } } /** * 相机位置 */ videoShed3d.prototype._changeCameraPosition = function (e) { if (e) { this.param.position = e; var option = this._initCameraParam(); this.cameraPosition = option.cameraPosition; } } videoShed3d.prototype._changeFar = function (e) { if (e) { this.param.far = e; var option = this._initCameraParam(); this.position = option.position; } } videoShed3d.prototype._changeNear = function (e) { if (e) { this.param.near = e; this.near = this.param.near; this._changeCameraPos(); } } /**获取三维地图容器像素大小 */ videoShed3d.prototype._getWinWidHei = function () { var viewer = this.viewer.scene; return viewer.canvas.clientWidth / viewer.canvas.clientHeight; } videoShed3d.prototype._changeCameraFov = function () { this.viewer.scene.postProcessStages.remove(this.postProcess) this.viewer.scene.primitives.remove(this.cameraFrustum), this._createShadowMap(this.cameraPosition, this.position), this._getOrientation(), this._addCameraFrustum(), this._addPostProcess() } videoShed3d.prototype._changeVideoWidHei = function () { this.viewer.scene.postProcessStages.remove(this.postProcess), this.viewer.scene.primitives.remove(this.cameraFrustum) this._createShadowMap(this.cameraPosition, this.position), this._getOrientation(), this._addCameraFrustum(), this._addPostProcess() } videoShed3d.prototype._changeCameraPos = function () { this.viewer.scene.postProcessStages.remove(this.postProcess), this.viewer.scene.primitives.remove(this.cameraFrustum), this.viewShadowMap.destroy(), this.cameraFrustum.destroy(), this._createShadowMap(this.cameraPosition, this.position), this._getOrientation(), this._addCameraFrustum(), this._addPostProcess() } videoShed3d.prototype._changeViewPos = function () { this.viewer.scene.postProcessStages.remove(this.postProcess), this.viewer.scene.primitives.remove(this.cameraFrustum), this.viewShadowMap.destroy(), this.cameraFrustum.destroy(), this._createShadowMap(this.cameraPosition, this.position), this._getOrientation(), this._addCameraFrustum(), this._addPostProcess() } videoShed3d.prototype._switchShow = function () { this.show ? !this.postProcess && this._addPostProcess() : (this.viewer.scene.postProcessStages.remove(this.postProcess), delete this.postProcess, this.postProcess = null), this.cameraFrustum.show = this.show } /** 创建视频Element * @param {String} url 视频地址 **/ videoShed3d.prototype._createVideoEle = function (url) { this.videoId = \"visualDomId\"; var t = document.createElement(\"SOURCE\"); t.type = \"video/mp4\", t.src = url; var i = document.createElement(\"SOURCE\"); i.type = \"video/quicktime\", i.src = url; var a = document.createElement(\"VIDEO\"); return a.setAttribute(\"autoplay\", !0), a.setAttribute(\"loop\", !0), a.setAttribute(\"crossorigin\", !0), a.appendChild(t), a.appendChild(i), //document.body.appendChild(a), this._videoEle = a, a } /** 视频投射 * @param {String} url 视频地址 */ videoShed3d.prototype.activeVideo = function (url) { var video = this._createVideoEle(url), that = this; if (video) { this.type = that.optionType.Video; var viewer = this.viewer; this.activeVideoListener || (this.activeVideoListener = function () { that.videoTexture && that.videoTexture.destroy(),  that.videoTexture = new Cesium.Texture({ context: viewer.scene.context, source: video, width: 1, height: 1, pixelFormat: Cesium.PixelFormat.RGBA, pixelDatatype: Cesium.PixelDatatype.UNSIGNED_BYTE  }) }), viewer.clock.onTick.addEventListener(this.activeVideoListener) } } videoShed3d.prototype.deActiveVideo = function () { if (this.activeVideoListener) { this.viewer.clock.onTick.removeEventListener(this.activeVideoListener), delete this.activeVideoListener } } /** 图片投放 * @param {String} url 图片地址 **/ videoShed3d.prototype.activePicture = function (url) { this.videoTexture = this.texture; var that = this, img = new Image; img.onload = function () { that.type = that.optionType.Image, that.videoTexture = new Cesium.Texture({  context: that.viewer.scene.context,  source: img }) }, img.onerror = function () { console.log(\'图片加载失败:\' + url) }, img.src = url } videoShed3d.prototype.locate = function () { var cameraPosition = Cesium.clone(this.cameraPosition), position = Cesium.clone(this.position); this.viewer.Camera.position = cameraPosition, this.viewer.camera.direction = Cesium.Cartesian3.subtract(position, cameraPosition, new Cesium.Cartesian3(0, 0, 0)), this.viewer.camera.up = Cesium.Cartesian3.normalize(cameraPosition, new Cesium.Cartesian3(0, 0, 0)) } videoShed3d.prototype.update = function (e) { this.viewShadowMap && this.viewer.scene.frameState.shadowMaps.push(this.viewShadowMap) // *重点* 多投影 } videoShed3d.prototype.destroy = function () { this.viewer.scene.postProcessStages.remove(this.postProcess), this.viewer.scene.primitives.remove(this.cameraFrustum), //this._videoEle && this._videoEle.parentNode.removeChild(this._videoEle), this.activeVideoListener && this.viewer.clock.onTick.removeEventListener(this.activeVideoListener), this.activeVideoListener && delete this.activeVideoListener, delete this.postProcess, delete this.viewShadowMap, delete this.color, delete this.viewDis, delete this.cameraPosition, delete this.position, delete this.alpha, delete this._camerafov, delete this._cameraPosition, delete this.videoTexture, delete this.cameraFrustum, delete this._videoEle, delete this._debugFrustum, delete this._position, delete this._aspectRatio, delete this.url, delete this.orientation, delete this.texture, delete this.videoId, delete this.type, this.viewer.scene.primitives.remove(this), delete this.viewer } // 创建shadowmap videoShed3d.prototype._createShadowMap = function () { var e = this.cameraPosition, t = this.position, i = this.viewer.scene, a = new Cesium.Camera(i); a.position = e, a.direction = Cesium.Cartesian3.subtract(t, e, new Cesium.Cartesian3(0, 0, 0)), //计算两个笛卡尔的组分差异。 a.up = Cesium.Cartesian3.normalize(e, new Cesium.Cartesian3(0, 0, 0)); // 归一化 var n = Cesium.Cartesian3.distance(t, e); this.viewDis = n, a.frustum = new Cesium.PerspectiveFrustum({ fov: Cesium.Math.toRadians(this.fov), aspectRatio: this.aspectRatio, near: this.near, far: n }); this.viewShadowMap = new Cesium.ShadowMap({ lightCamera: a, enable: !1, isPointLight: !1, isSpotLight: !0, cascadesEnabled: !1, context: i.context, pointLightRadius: n }) } // 获取shadowmap位置 videoShed3d.prototype._getOrientation = function () { var e = this.cameraPosition, t = this.position, i = Cesium.Cartesian3.normalize(Cesium.Cartesian3.subtract(t, e, new Cesium.Cartesian3), new Cesium.Cartesian3), a = Cesium.Cartesian3.normalize(e, new Cesium.Cartesian3), n = new Cesium.Camera(this.viewer.scene); n.position = e, n.direction = i, n.up = a, i = n.directionWC, a = n.upWC; var r = n.rightWC, o = new Cesium.Cartesian3, l = new Cesium.Matrix3, u = new Cesium.Quaternion; r = Cesium.Cartesian3.negate(r, o); var d = l; Cesium.Matrix3.setColumn(d, 0, r, d), Cesium.Matrix3.setColumn(d, 1, a, d), Cesium.Matrix3.setColumn(d, 2, i, d); var c = Cesium.Quaternion.fromRotationMatrix(d, u); /*var viewMatrix=n.viewMatrix; var inverseViewMatrix=n.inverseViewMatrix; console.log(\"视图矩阵=\",viewMatrix); console.log(\"逆视图矩阵=\",inverseViewMatrix); var frustum = new Cesium.PerspectiveFrustum({ fov :20, aspectRatio : 0.75, near : 1.0, far : 10.0 }); var projectionMatrix=frustum.projectionMatrix; var infiniteProjectionMatrix=frustum.infiniteProjectionMatrix; console.log(\"投影矩阵=\",projectionMatrix); console.log(\"透视投影矩阵=\",infiniteProjectionMatrix);  //透视投 影矩阵反转 var inverseInfiniteProjectionMatrix=new Cesium.Matrix4(); Cesium.Matrix4.inverse(infiniteProjectionMatrix,inverseInfiniteProjectionMatrix); console.log(\"透视投 影矩阵反转=\",inverseInfiniteProjectionMatrix);  //逆视图投影矩阵 var inverseViewProjectionMatrix=new Cesium.Matrix4(); Cesium.Matrix4.multiply(inverseInfiniteProjectionMatrix,inverseViewMatrix,inverseViewProjectionMatrix) console.log(\"逆视图投影矩阵=\",inverseViewProjectionMatrix);  //视图投影矩阵 var viewProjectionMatrix=new Cesium.Matrix4(); Cesium.Matrix4.inverse(inverseViewProjectionMatrix,viewProjectionMatrix); console.log(\"视图投影矩阵=\",viewProjectionMatrix);  //远平面标准模型矩阵 var matrix4 = Cesium.Matrix4.fromUniformScale(10); console.log(\"远平面标准模型矩阵=\",matrix4);  //模型矩阵 var modelMatrix=new Cesium.Matrix4(); Cesium.Matrix4.multiply(inverseViewMatrix,matrix4,modelMatrix) console.log(\"模型矩阵=\",modelMatrix);  //视图矩阵与逆视图投影矩阵相乘得到立方体模型视图 var uBoxMV=new Cesium.Matrix4(); Cesium.Matrix4.multiply(viewMatrix,inverseViewProjectionMatrix,uBoxMV) console.log(\"立方体模型视图=\",uBoxMV);  //逆立方体模型视图 var uInverseBoxMV=new Cesium.Matrix4(); Cesium.Matrix4.multiply(viewMatrix,viewProjectionMatrix,uInverseBoxMV) console.log(\"立方体模型视图=\",uInverseBoxMV);  //将这两个模型视图赋予分类基元类的一致性映射 参数便可以最终实现视频监控图像与实景三维场景的融 合 var geometry =this.creacteGeometry(5,5); var instance = new Cesium.GeometryInstance({  // geometry: //new Cesium.Geometry({}),  // geometry: new Cesium.GeometryInstance({  // geometry:new Cesium.FrustumOutlineGeometry ({  // origin: Cesium.Cartesian3.fromDegrees(cameraLong,cameraLat, cameraHeight),  // orientation:orientation,  // frustum: perspectiveFrustum,  // _drawNearPlane: true  // }),  geometry:geometry,  classificationType:Cesium.ClassificationType.BOTH,  // modelMatrix: modelMatrix,  attributes : {  color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.fromCssColorString(\'#ff0000\').withAlpha(1.0)),  show : new Cesium.ShowGeometryInstanceAttribute(true)  }  }); var videoElement = this._createVideoEle(\"http://localhost:7070/video/北京路与天马路交叉口高点枪机.mkv\"); var material = Cesium.Material.fromType(\'Image\'); material.uniforms.image = videoElement; var _uniformMap ={  u_boxMV:uBoxMV,  u_inverseBoxMV:uInverseBoxMV  }; this.viewer.scene.primitives.add(new Cesium.Primitive({  geometryInstances: instance,  appearance: new Cesium.MaterialAppearance ({  material: material,  close:false,  }),  modelMatrix: modelMatrix,  _uniformMap:_uniformMap,  asynchronous:false,  compressVertices:false,  allowPicking:false }));*/ //ClassificationPrimitive return this.orientation = c, c } videoShed3d.prototype.creacteGeometry = function (width, height) { var hwidth = width / 2.0; var hheigt = height / 2.0; var positions = new Float64Array([hwidth, 0.0, hheigt, -hwidth, 0.0, hheigt, -hwidth, 0.0, -hheigt, hwidth, 0.0, -hheigt]); var sts = new Float32Array([1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0]); var indices = new Uint16Array([0, 1, 2, 0, 2, 3]); var ge = this._createGeometry(positions, sts, indices); return ge; }, videoShed3d.prototype._createGeometry = function (positions, sts, indices) { /* var Cesium = this.Cesium;*/ return new Cesium.Geometry({ attributes: { position: new Cesium.GeometryAttribute({  componentDatatype: Cesium.ComponentDatatype.DOUBLE,  componentsPerAttribute: 3,  values: positions }), normal: new Cesium.GeometryAttribute({  componentDatatype: Cesium.ComponentDatatype.FLOAT,  componentsPerAttribute: 3,  values: new Float32Array([255.0, 0.0, 0.0, 255.0, 0.0, 0.0, 255.0, 0.0, 0.0, 255.0, 0.0, 0.0])  // values: new Float32Array([0.0, 0.0, 0.0,0.0, 0.0, 0.0,0.0, 0.0, 0.0,0.0, 0.0, 0.0]) }), st: new Cesium.GeometryAttribute({  componentDatatype: Cesium.ComponentDatatype.FLOAT,  componentsPerAttribute: 2,  values: sts }) }, indices: indices, primitiveType: Cesium.PrimitiveType.TRIANGLES, vertexFormat: new Cesium.VertexFormat({ position: true, color: true }), boundingSphere: Cesium.BoundingSphere.fromVertices(positions) }); }, //创建视锥 videoShed3d.prototype._addCameraFrustum = function () { var e = this; this.cameraFrustum = new Cesium.Primitive({ geometryInstances: new Cesium.GeometryInstance({ geometry: new Cesium.FrustumOutlineGeometry({  origin: e.cameraPosition,  orientation: e.orientation,  frustum: this.viewShadowMap._lightCamera.frustum,  _drawNearPlane: !0 }), attributes: {  color: Cesium.ColorGeometryInstanceAttribute.fromColor(new Cesium.Color(0, 0.5, 0.5)) } }), appearance: new Cesium.PerInstanceColorAppearance({ translucent: !1, flat: !0 }), asynchronous: !1, show: this.debugFrustum && this.show }), this.viewer.scene.primitives.add(this.cameraFrustum) } videoShed3d.prototype._addPostProcess = function () { var e = this, t = videoShed3dShader, i = e.viewShadowMap._isPointLight ? e.viewShadowMap._pointBias : e.viewShadowMap._primitiveBias; this.postProcess = new Cesium.PostProcessStage({ fragmentShader: t, uniforms: { mixNum: function () {  return e.alpha }, stcshadow: function () {  return e.viewShadowMap._shadowMapTexture }, videoTexture: function () {  return e.videoTexture }, _shadowMap_matrix: function () {  return e.viewShadowMap._shadowMapMatrix }, shadowMap_lightPositionEC: function () {  return e.viewShadowMap._lightPositionEC }, shadowMap_texelSizeDepthBiasAndNormalShadingSmooth: function () {  var t = new Cesium.Cartesian2;  return t.x = 1 / e.viewShadowMap._textureSize.x, t.y = 1 / e.viewShadowMap._textureSize.y, Cesium.Cartesian4.fromElements(t.x, t.y, i.depthBias, i.normalShadingSmooth, this.combinedUniforms1) }, shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness: function () {  return Cesium.Cartesian4.fromElements(i.normalOffsetScale, e.viewShadowMap._distance, e.viewShadowMap.maximumDistance, e.viewShadowMap._darkness, this.combinedUniforms2) } } }), this.viewer.scene.postProcessStages.add(this.postProcess); } return videoShed3d;})()// export default CesiumVideo3d

2、CoordinateTranslate.js(同上,也是技术群大佬分享的)

let ECEF = (function () {var _=function () {this.PI = 3.141592653589793238;this.a = 6378137.0;this.b = 6356752.3142this.f = (this.a - this.b) / this.a;this.e_sq = this.f * (2.0 - this.f);this.ee = 0.00669437999013;this.WGSF = 1 / 298.257223563;this.WGSe2 = this.WGSF * (2 - this.WGSF);this.WGSa = 6378137.00000;this.EPSILON = 1.0e-12;}_.prototype.CalculateCoordinates = function (point, azimuth, elevation, distance) {var vertical_height = distance * Math.sin(2 * this.PI / 360 * elevation);//垂直高度var horizontal_distance = distance * Math.cos(2 * this.PI / 360 * elevation);//水平距离if (azimuth > 360) azimuth = azimuth % 360;if (azimuth < 0) azimuth = 360 + (azimuth % 360);var point1 = this.lonLat2WebMercator(point);var lnglat = null;var x_length , y_lengthif (azimuth <= 90) {//第四象限x_length = horizontal_distance * Math.cos(2 * this.PI / 360 * azimuth);y_length = horizontal_distance * Math.sin(2 * this.PI / 360 * azimuth);lnglat = {x: point1.x + x_length,y: point1.y - y_length}} else if (azimuth > 90 && azimuth <= 180) {//第三象限x_length = horizontal_distance * Math.sin(2 * this.PI / 360 * (azimuth - 90));y_length = horizontal_distance * Math.cos(2 * this.PI / 360 * (azimuth - 90));lnglat = {x: point1.x - x_length,y: point1.y - y_length}} else if (azimuth > 180 && azimuth <= 270) {//第二象限x_length = horizontal_distance * Math.cos(2 * this.PI / 360 * (azimuth - 180));y_length = horizontal_distance * Math.sin(2 * this.PI / 360 * (azimuth - 180));lnglat = {x: point1.x - x_length,y: point1.y + y_length}} else {//第一象限x_length = horizontal_distance * Math.sin(2 * this.PI / 360 * (azimuth - 270));y_length = horizontal_distance * Math.cos(2 * this.PI / 360 * (azimuth - 270));lnglat = {x: point1.x + x_length,y: point1.y + y_length}}lnglat = this.webMercator2LonLat(lnglat);return {lng: lnglat.x,lat: lnglat.y,height: vertical_height}}/* *经纬度转Web墨卡托 *@lonLat 经纬度 */_.prototype.lonLat2WebMercator = function (lonLat) {let x = lonLat.x * this.a / 180;let y = Math.log(Math.tan((90 + lonLat.y) * this.PI / 360)) / (this.PI / 180);y = y * this.a / 180;return {x: x,y: y}}/* *Web墨卡托转经纬度 *@mercator 平面坐标 */_.prototype.webMercator2LonLat = function (mercator) {let x = mercator.x / this.a * 180;let y = mercator.y / this.a * 180;y = 180 / this.PI * (2 * (Math.exp(y * this.PI / 180)) - this.PI / 2);return {x: x,y: y}}_.prototype.get_atan = function (z, y) {let x;if (z == 0) {x = this.PI / 2;} else {if (y == 0) {x = this.PI;} else {x = Math.atan(Math.abs(y / z));if ((y > 0) && (z < 0)) {x = this.PI - x;} else if ((y < 0) && (z < 0)) {x = this.PI + x;} else if ((y < 0) && (z > 0)) {x = 2 * this.M_PI - x;}}}return x;}//WGS84转ECEF坐标系_.prototype.ConvertLLAToXYZ = function (LLACoor) {let lon = this.PI / 180 * LLACoor.longitude;let lat = this.PI / 180 * LLACoor.latitude;let H = LLACoor.altitude;let N0 = this.a / Math.sqrt(1.0 - this.ee * Math.sin(lat) * Math.sin(lat));let x = (N0 + H) * Math.cos(lat) * Math.cos(lon);let y = (N0 + H) * Math.cos(lat) * Math.sin(lon);let z = (N0 * (1.0 - this.ee) + H) * Math.sin(lat);return {x: x,y: y,z: z}}//ECEF坐标系转WGS84_.prototype.ConvertXYZToLLA = function (XYZCoor) {let longitude = this.get_atan(XYZCoor.x, XYZCoor.y);if (longitude < 0) {longitude = longitude + this.PI;}let latitude = this.get_atan(Math.sqrt(XYZCoor.x * XYZCoor.x + XYZCoor.y * XYZCoor.y), XYZCoor.z);let W = Math.sqrt(1 - this.WGSe2 * Math.sin(latitude) * Math.sin(latitude));let N = this.WGSa / W;let B1;do {B1 = latitude;W = Math.sqrt(1 - this.WGSe2 * Math.sin(B1) * Math.sin(B1));N = this.WGSa / W;latitude = this.get_atan(Math.sqrt(XYZCoor.x * XYZCoor.x + XYZCoor.y * XYZCoor.y), (XYZCoor.z + N * this.WGSe2 * Math.sin(B1)));}while (Math.abs(latitude - B1) > this.EPSILON);var altitude = Math.sqrt(XYZCoor.x * XYZCoor.x + XYZCoor.y * XYZCoor.y) / Math.cos(latitude) - this.WGSa / Math.sqrt(1 - this.WGSe2 * Math.sin(latitude) * Math.sin(latitude));return {longitude: longitude * 180 / this.PI,latitude: latitude * 180 / this.PI,altitude: altitude}}/*北东天坐标系转WGS84@ a A点坐标@ p 相对参数,距离、方位角、仰角*///俯视角pitch -elevation //航向角heading(yaw) -azimuth _.prototype.enu_to_ecef = function (a, p) {//距离let distance = p.distance;//方位角let azimuth = p.azimuth;//仰角let elevation = p.elevation;let zUp = elevation >= 0 ? distance * Math.sin(this.PI / 180 * elevation) : (-1) * distance * Math.sin(this.PI / 180 * Math.abs(elevation));let d = distance * Math.cos(this.PI / 180 * Math.abs(elevation));let xEast;let yNorth;if (azimuth <= 90) {xEast = d * Math.sin(this.PI / 180 * azimuth);yNorth = d * Math.cos(this.PI / 180 * azimuth);} else if (azimuth > 90 && azimuth < 180) {xEast = d * Math.cos(this.PI / 180 * (azimuth - 90));yNorth = (-1) * d * Math.sin(this.PI / 180 * (azimuth - 90));} else if (azimuth > 180 && azimuth < 270) {xEast = (-1) * d * Math.sin(this.PI / 180 * (azimuth - 180));yNorth = (-1) * d * Math.cos(this.PI / 180 * (azimuth - 180));} else {xEast = (-1) * d * Math.sin(this.PI / 180 * (360 - azimuth));yNorth = d * Math.cos(this.PI / 180 * (360 - azimuth));}let lamb = this.radians(a.latitude);let phi = this.radians(a.longitude);let h0 = a.altitude;let s = Math.sin(lamb);let N = this.a / Math.sqrt(1.0 - this.e_sq * s * s);let sin_lambda = Math.sin(lamb);let cos_lambda = Math.cos(lamb);let sin_phi = Math.sin(phi);let cos_phi = Math.cos(phi);let x0 = (h0 + N) * cos_lambda * cos_phi;let y0 = (h0 + N) * cos_lambda * sin_phi;let z0 = (h0 + (1 - this.e_sq) * N) * sin_lambda;let t = cos_lambda * zUp - sin_lambda * yNorth;let zd = sin_lambda * zUp + cos_lambda * yNorth;let xd = cos_phi * t - sin_phi * xEast;let yd = sin_phi * t + cos_phi * xEast;return this.ConvertXYZToLLA({x: xd + x0,y: yd + y0,z: zd + z0})}_.prototype.radians = function (degree) {return this.PI / 180 * degree;}return _})()// export default CoordinateTranslate

3、index.html

<!DOCTYPE html><html lang=\"en\"><head> <meta charset=\"utf-8\"> <meta http-equiv=\"X-UA-Compatible\" content=\"IE=Edge,chrome=1\"> <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no\"> <title>无人机视频投影</title> <script src=\"../Cesium/Cesium.js\"></script> <script src=\"../jquery-2.1.4.js\"></script> <script src=\"../CoordinateTranslate.js\"></script> <script src=\"../CesiumVideo3d.js\"></script> <style> @import url(\'../Cesium/Widgets/widgets.css\'); html, body, #cesiumContainer { width: 100%; height: 100%; margin: 0; padding: 0; overflow: hidden; } </style></head><body><div id=\"cesiumContainer\"></div><script> // 初始化 Cesium Cesium.Ion.defaultAccessToken = \'你的cesium token令牌\' const viewer = new Cesium.Viewer(\'cesiumContainer\', { imageryProvider: new Cesium.ArcGisMapServerImageryProvider({ url: \"https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer\", maximumLevel: 18, }), terrainProvider: Cesium.createWorldTerrain({ requestVertexNormals: true, requestWaterMask: true }), shouldAnimate: true, selectionIndicator: false, infoBox: false, geocoder: false, baseLayerPicker: false, timeline: false, homeButton: false, fullscreenButton: false, animation: false, sceneModePicker: false, navigationHelpButton: false, }); viewer.cesiumWidget.creditContainer.style.display = \"none\"; viewer.scene.globe.depthTestAgainstTerrain = true; viewer.scene.globe.enableLighting = true; // 设置初始视角以更好地观察地形 viewer.camera.setView({ destination: Cesium.Cartesian3.fromDegrees( 120.619907115, // 经度 31.317987368, // 纬度 2000 // 高度 ), orientation: { heading: 0.0, pitch: Cesium.Math.toRadians(-45), roll: 0.0 } }); // 添加高清影像图层 viewer.imageryLayers.addImageryProvider(new Cesium.IonImageryProvider({ assetId: 3 // Bing Maps Aerial 影像 })); // 初始化无人机位置和视频投影 let dronePosition = { lng: 120.619907115, lat: 31.317987368, height: 500 }; // 创建无人机模型实体 const drone = viewer.entities.add({ position: Cesium.Cartesian3.fromDegrees(dronePosition.lng, dronePosition.lat, dronePosition.height), model: { uri: \'../CesiumDrone.glb\',//你的无人机模型 minimumPixelSize: 100, maximumScale: 2000, scale: 0.5, }, orientation: new Cesium.CallbackProperty(() => { // 添加90度偏移来修正朝向 const headingOffset = Cesium.Math.toRadians(-90); return Cesium.Transforms.headingPitchRollQuaternion( Cesium.Cartesian3.fromDegrees(dronePosition.lng, dronePosition.lat, dronePosition.height), new Cesium.HeadingPitchRoll(droneHeading + headingOffset, 0, 0) ); }, false) }); // 初始化视频投影 let videoProjection = new CesiumVideo3d(Cesium, viewer, { position: { x: dronePosition.lng, // 投影相机位置的经度 y: dronePosition.lat, // 投影相机位置的纬度 z: dronePosition.height // 投影相机位置的高度(单位:米) }, far: 1000, // 视锥体远裁剪面距离,决定投影的最大距离(单位:米) rotation: { y: 0, // 航向角,决定投影方向的水平旋转角度(0度为正北,90度为正东) x: -45 // 俯仰角,决定投影的垂直倾斜角度(-90度为垂直向下,0度为水平) }, url: \'../demo.mp4\', // 要投影的视频文件路径 alpha: 0.8, // 投影的透明度(0-1之间,1为完全不透明) near: 0.5, // 视锥体近裁剪面距离,决定投影的最小距离(单位:米) fov: 60, // 视场角,决定投影的张角大小(单位:度,值越大投影范围越大) debugFrustum: true // 是否显示视锥体的线框,用于调试 }); // 删除 WebSocket 相关代码,替换为 setInterval let droneHeading = 0; let lastPosition = null; let time = 0; // 使用 setInterval 模拟位置更新 setInterval(() => { time += 0.05; // 模拟圆形飞行路径 const radius = 0.005; const centerLng = 120.619907115; const centerLat = 31.317987368; // 计算新位置 const newLng = centerLng + radius * Math.cos(time); const newLat = centerLat + radius * Math.sin(time); // 获取地形高度 const positions = [Cesium.Cartographic.fromDegrees(newLng, newLat)]; // 使用 Promise 替代 Cesium.when Cesium.sampleTerrainMostDetailed(viewer.terrainProvider, positions) .then(function(updatedPositions) { // 地形高度加上偏移量作为飞行高度 const terrainHeight = updatedPositions[0].height || 0; const flyHeight = terrainHeight + 300; // 300米的相对高度 const newPosition = {  lng: newLng,  lat: newLat,  height: flyHeight }; // 更新无人机位置 dronePosition = newPosition; // 计算航向角 if (lastPosition) {  const deltaLng = dronePosition.lng - lastPosition.lng;  const deltaLat = dronePosition.lat - lastPosition.lat;  droneHeading = Math.atan2(deltaLng, deltaLat); } lastPosition = {...dronePosition}; // 更新无人机实体位置 drone.position = Cesium.Cartesian3.fromDegrees(  dronePosition.lng,  dronePosition.lat,  dronePosition.height ); // 更新视频投影位置和方向 videoProjection._changeCameraPosition({  x: dronePosition.lng,  y: dronePosition.lat,  z: dronePosition.height }); videoProjection._changeRotation({  y: Cesium.Math.toDegrees(droneHeading),  x: -45 }); // 相机跟随 if(time == 0.2) {  viewer.camera.setView({ destination: Cesium.Cartesian3.fromDegrees( dronePosition.lng, dronePosition.lat, dronePosition.height + 1000 ), orientation: { heading: droneHeading, pitch: Cesium.Math.toRadians(-45), roll: 0 }  }); } }) .catch(function(error) { console.error(\'Error sampling terrain:\', error); }); }, 50); // 每50毫秒更新一次位置 // 修改清理函数 function cleanup() { if (videoProjection) { videoProjection.destroy(); } viewer.entities.remove(drone); } // 页面关闭时清理资源 window.onbeforeunload = cleanup;</script></body></html> 

有机奶粉