Cesium实现动态旋转四棱锥(2023.9.11)

1、引言

        最近看到一些数字城市特效,其中包含四棱锥动态旋转的效果,乍一看眼前一亮,静下来来冷静思考觉得实现起来应该也十分简单,于是打算写此博客与诸位开发者共同分享,当然也是为了记录学习点滴!🕺🕺🕺❤️❤️❤️

2、两种实现思路介绍

        顾名思义,动态悬浮旋转四棱锥效果中的关键词包括:四棱锥(金字塔)、旋转、动态

2.1 思路一:添加已有的四棱锥(金字塔)模型实现(简单但受限)

        寻找并选择一个现成的四棱锥模型(gltfglb文件),调用Cesium添加Model的API将其作为模型加载到三维场景当中,之后动态设置旋转角度、位置高度即可。
        假设准备好的模型文件为pyramid.glb文件,利用Windows系统自带的3D查看器通过设置跳跃、悬浮等动作即可预览动态效果。

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

2.2 思路二:自定义四棱锥几何模型实现(复杂且灵活)

        调用Cesium底层API自定义几何形状(Geometry)和原型(Primitive),构造属于四棱锥的DrawCommand类实现create方法在三维场景Scene中添加四棱锥几何形状并设置纹理显示,实现update方法在每一帧画面中更新显示动态旋转及上下悬浮效果。

图1 四棱锥几何立体示意
注:红色圆圈代表顶点、橙色数字代表顶点索引编号、蓝色实线代表各边。

        值得注意的是,我们仍需明确有关正四棱锥的一些数学理论知识:在三维立体几何空间中,四棱锥包含5个顶点、6个三角面(1个四边形可拆分为2个三角形),每个顶点包含X、Y、Z这三个坐标。如果给所有顶点从0开始进行顺序编号,那么各个三角面就能根据三个顶点索引随着确定,相应地纹理赋值也能随之确定。

3、代码实现及效果展示

        接下来将具体调用CesiumAPI按照上述两种思路分别进行实现,具体代码如下:

3.1 思路一

3.1.1 代码实现


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

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Cesium旋转金字塔-jing_zhong</title>
  <!-- 引入Cesium -->
  <script src="https://unpkg.com/cesium@1.84.0/Build/Cesium/Cesium.js"></script>
  <link rel="stylesheet" href="https://unpkg.com/cesium@1.84.0/Build/Cesium/Widgets/widgets.css">
  <script src="https://unpkg.com/vue@2.6.14/dist/vue.min.js"></script>
  <link rel="stylesheet" href="https://unpkg.com/element-ui@2.15.5/lib/theme-chalk/index.css">
  <script src="https://unpkg.com/element-ui@2.15.5/lib/index.js"></script>
  <style>
    * {
      margin: 0;
      padding: 0;
    }

    html,
    body,
    #viewer-container {
      width: 100%;
      height: 100%;
      overflow: hidden;
    }

    .cesium-widget-credits{ 
      display:none!important; 
      visibility:hide!important; 
    }
    .cesium-viewer-toolbar{
      display:none!important; 
      visibility:hide!important;
    }
    .form-container {
      position: absolute;
      left: 10px;
      top: 90px;
      padding: 10px 15px;
      border-radius: 4px;
      border: 1px solid rgba(128, 128, 128, 0.5);
      color: #ffffff;
      background: rgba(0, 0, 0, 0.4);
      box-shadow: 0 3px 14px rgb(128 128 128 / 50%);
      max-width: 380px;
    }
    button {
      background: transparent;
      border: 1px solid #00d0ffb8;
      color: white;
      padding: 7px 9px;
      border-radius: 2px;
      margin: 3px;
      cursor: pointer
    }
    .tip-item {
      margin: 2px 0px;
      padding: 5px 1px;
    }
  </style>
</head>

<body>
  <div id="viewer-container"></div>
  <div class="form-container" id="formContainer">
    <button onclick="setvisible('add')" style="margin-left:120px;">添加旋转金字塔</button>
    <button onclick="setvisible('remove')">移除旋转金字塔</button>
  </div>
  <script>
    var viewer = null;
    var modelEntity = null;

    // 开关
    function setvisible(value) {
      switch (value) {
        case 'add':
          addPyramidModel();
          break;
        case 'remove':
          removeRotateCircle();
          break;
      }
    }
    // 添加旋转金字塔
    function addPyramidModel() {
        let hpr = new Cesium.HeadingPitchRoll(
            Cesium.Math.toRadians(0),
            Cesium.Math.toRadians(180),//0朝下 180朝上
            Cesium.Math.toRadians(0)
        )
        let r = Cesium.Math.toRadians(2);
        let lon = 121.50320483066757, lat = 31.23641093043576, height = 382.83983348350085,isUp = true;
        // let position = Cesium.Cartesian3.fromDegrees(121.50320483066757, 31.23641093043576, 382.83983348350085);
        modelEntity = this.viewer.entities.add({
            position:  new Cesium.CallbackProperty(e => {
              if(height > 400)
              {
                height = 400;
                isUp = false;

              }
              else if(height < 350)
              {
                height = 350;
                isUp = true;
              }
              if(isUp)
              {
                height += 1.0;
              }
              else
              {
                height -= 1.0;
              }
              
              return Cesium.Cartesian3.fromDegrees(lon,lat,height);
            }, false),
            //旋转起来
            orientation: new Cesium.CallbackProperty(e => {
                window.console.log(e);
                hpr.heading += r;
               
                let position = Cesium.Cartesian3.fromDegrees(lon,lat, height);
                return Cesium.Transforms.headingPitchRollQuaternion(position, hpr);
            }, false),
            model: {
                uri: "pyramid.glb",
                scale: 40,
                color: Cesium.Color.YELLOW.withAlpha(0.8),
                colorBlendMode: Cesium.ColorBlendMode.MIX,
            }
        });
    
      viewer.flyTo(modelEntity);
    }

    // 移除旋转金字塔
    function removeRotateCircle() {
      if(modelEntity != null)
      {
        viewer.entities.remove(modelEntity);
        modelEntity.destroy();
        modelEntity = null;
      }
    }

    // init
    function initPage() {
      // 切换自己的token
      Cesium.Ion.defaultAccessToken = 'your_token';
      // 初始化
      viewer = new Cesium.Viewer("viewer-container", {
        infoBox: false,
        shouldAnimate: true,
        vrButton: true,
        geocoder: false,
        homeButton: false,
        sceneModePicker: false,
        baseLayerPicker: false,
        navigationHelpButton: false,
        animation: false,//动画控制不显示
        timeline: false,//时间线不显示
        fullscreenButton: false,//全屏按钮不显示
        terrainProvider: Cesium.createWorldTerrain({
           requestWaterMask: true, // 水特效
           requestVertexNormals: true // 地形光
        }),
      });

      viewer.scene.primitives.add(new Cesium.Cesium3DTileset({
        url: './dayanta/tileset.json',
        show: true,
        backFaceCulling: true,
       })
      ).readyPromise.then((tileset) => {
        //拉伸模型高度代码
        let heightOffset = -26;
        var boundingSphere = tileset.boundingSphere;
        var cartographic = Cesium.Cartographic.fromCartesian(boundingSphere.center);
        var surface = Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, 0.0);
        var offset = Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, heightOffset);
        var translation = Cesium.Cartesian3.subtract(offset, surface, new Cesium.Cartesian3());
        tileset.modelMatrix = Cesium.Matrix4.fromTranslation(translation);
        //viewer.zoomTo(tileset)
        viewer.flyTo(tileset);
      })
    }
    // 
    window.onload = function () {
      initPage();
    }
  </script>
</body>
</html>

3.1.2 展示结果

3.2 思路二

3.2.1 代码实现


<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <style>
    .cesium-widget-credits{ 
      display:none!important; 
      visibility:hide!important; 
    }
    .cesium-viewer-toolbar{
      display:none!important; 
      visibility:hide!important;
    }
    .middleTop {
      width: 300px;
      height: 30px;
      position: fixed;
      top: 10px;
      left: 20px;
      text-align: center;
      background: red;
      opacity: 0.6;
    }
    button {
      background: gray;
      border: 1px solid #00d0ffb8;
      color: white;
      padding: 7px 9px;
      border-radius: 2px;
      margin: 3px;
      cursor: pointer
    }
    .tip-item {
      margin: 2px 0px;
      padding: 5px 1px;
    }
  </style>
  <script src="https://cesium.com/downloads/cesiumjs/releases/1.89/Build/Cesium/Cesium.js"></script>
  <link href="https://cesium.com/downloads/cesiumjs/releases/1.89/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
  <!-- <script src="https://cesium.com/downloads/cesiumjs/releases/1.108/Build/Cesium/Cesium.js"></script>
  <link href="https://cesium.com/downloads/cesiumjs/releases/1.108/Build/Cesium/Widgets/widgets.css" rel="stylesheet"> -->
</head>
<body>
  <div id="cesiumContainer" style="width:100%;height:100%;">
  </div>
  <div class="middleTop" id="demo2">
    <div class="map-tool">
      <button id="addTetrahedron" class="newBtn">添加倒立四棱锥</button>
      <button id="removeTetrahedron" class="newBtn">移除一个倒立四棱锥</button>
    </div>
  </div>
  <script>
    Cesium.Ion.defaultAccessToken = "your_token";
    const viewer = new Cesium.Viewer('cesiumContainer', { // Initialize the Cesium Viewer in the HTML element with the `cesiumContainer` ID.
      baseLayerPicker: false,
        //shadows: true,
        shouldAnimate: true,
      infoBox: false,
      animation: false,//动画控制不显示
      timeline: false,//时间线不显示
      fullscreenButton: false, //全屏按钮不显示
      terrainProvider: Cesium.createWorldTerrain({
        requestWaterMask: true, // 水特效
        requestVertexNormals: true // 地形光
      }),
      selectionIndicator: false, // By jing_zhong 2022.9.21 移除Cesium自带的绿色聚焦瞄准框
       //imageryProvider: new Cesium.ArcGisMapServerImageryProvider({
       //  url: 'http://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer',
       //}),
    });
    viewer._cesiumWidget._creditContainer.style.display = "none";//去除版权信息
    //viewer.scene.globe.depthTestAgainstTerrain = true;
    let silhouette = null,skylineAnayStages = null;//天际线分析工具
    //打开天际线分析
    function openSkylineAnay() {
      if(skylineAnayStages){
        silhouette.enabled=true;
        return;
      }
      skylineAnayStages = viewer.scene.postProcessStages;
      let edgeDetection = Cesium.PostProcessStageLibrary.createEdgeDetectionStage();
      let postProccessStage = new Cesium.PostProcessStage({
        //此后处理阶段的唯一名称,供组合中其他阶段参考,如果未提供名称,将自动生成GUID
        // name:name,
        //unform着色器对象 textureScale
        fragmentShader: 'uniform sampler2D colorTexture;' +
            'uniform sampler2D depthTexture;' +
            'varying vec2 v_textureCoordinates;' +
            'void main(void)' +
            '{' +
            'float depth = czm_readDepth(depthTexture, v_textureCoordinates);' +
            'vec4 color = texture2D(colorTexture, v_textureCoordinates);' +
            'if(depth<1.0 - 0.000001){' +
            'gl_FragColor = color;' +
            '}' +
            'else{' +
            'gl_FragColor = vec4(1.0,0.0,0.0,1.0);' +
            '}' +
            '}'
      });
 
      //PostProcessStage:要使用的片段着色器。默认sampler2D制服是colorTexture和depthTexture。
      let postProccesStage_1 = new Cesium.PostProcessStage({
        // name:obj.name+'_1',
        fragmentShader: 'uniform sampler2D colorTexture;' +
            'uniform sampler2D redTexture;' +
            'uniform sampler2D silhouetteTexture;' +
            'varying vec2 v_textureCoordinates;' +
            'void main(void)' +
            '{' +
            'vec4 redcolor=texture2D(redTexture, v_textureCoordinates);' +
            'vec4 silhouetteColor = texture2D(silhouetteTexture, v_textureCoordinates);' +
            'vec4 color = texture2D(colorTexture, v_textureCoordinates);' +
            'if(redcolor.r == 1.0){' +
            'gl_FragColor = mix(color, vec4(5.0,0.0,0.0,1.0), silhouetteColor.a);' +
            '}' +
            'else{' +
            'gl_FragColor = color;' +
            '}' +
            '}',
        //uniform着色器对象
        uniforms: {
          redTexture: postProccessStage.name,
          silhouetteTexture: edgeDetection.name
        }
      });
 
      //如果inputPreviousStageTexture 是 true,则每个阶段输入是场景渲染到的输出纹理或之前执行阶段的输出纹理
      //如果inputPreviousStageTexture是false,则合成中每个阶段的输入纹理都是相同的
      silhouette= new Cesium.PostProcessStageComposite({
        stages:[edgeDetection,postProccessStage,postProccesStage_1], //PostProcessStage要按顺序执行 的 s 或组合的数组。
        inputPreviousStageTexture:false,//是否执行每个后处理阶段,其中一个阶段的输入是前一个阶段的输出。否则每个包含阶段的输入是组合之前执行的阶段的输出
        uniforms:edgeDetection.uniforms//后处理阶段制服的别名
      })
      skylineAnayStages.add(silhouette);
    };
    
    function closeSkylineAnay(){ //关闭天际线分析
      if(silhouette != null)
        silhouette.enabled=false;
    };

      // 添加四棱锥
      document.getElementById("addTetrahedron").addEventListener("click", function (e) {
        onLineTetra();// By jing_zhong 2023.8.31
      });
      // 移除四棱锥
      document.getElementById("removeTetrahedron").addEventListener("click", function (e) {
        removeSinglePrimitive();// By jing_zhong 2023.8.31
      });

      let addedPrimitives = [];
      function onLineTetra() // By jing_zhong 2023.8.31 源自 Mars3d源码启发 和 网上资料
      {
        function TetrahedronPrimitive(options){
          this.show = true;
          this._command = undefined;
          this._enuMatrix=undefined;
          this._scaleMatrix=undefined;
          this._localPosition = options.position;
          this._createCommand = createCommand;
          this._angle=0;
          this._distance= Cesium.defaultValue(options.distance,1);
          this._setInterval=undefined;
          this._viewer= viewer;
          this._speed= Cesium.defaultValue(options.speed,1.0);
          this._color= Cesium.defaultValue(options.color,new  Cesium.Color(1.0,0.0,0.0,0.8));
          this._scale= Cesium.defaultValue(options.scale,new  Cesium.Cartesian3(10, 10, 15));
          this._texture=undefined;
          // this._imageUrl= Cesium.buildModuleUrl('./fence.png');
          this._modelMatrix=computeModelMatrix(this);
          this._height=computeHeight(this);
          //debugger
          // createTexture(this);
      }
      TetrahedronPrimitive.prototype.update=function(frameState) {
          if (!this.show)
          {
              return;
          }
          if (! Cesium.defined(this._command))
          {
              this._command = this._createCommand(frameState.context,this);
              this._command.pickId = 'v_pickColor';
          }
          if ( Cesium.defined(this._command))
          {
              frameState.commandList.push(this._command);
          }
      }
      TetrahedronPrimitive.prototype.isDestroyed=function() {
          return false;
      }
      TetrahedronPrimitive.prototype.destroy=function() {
          if ( Cesium.defined(this._command))
          {
              this._command.shaderProgram = this._command.shaderProgram && this._command.shaderProgram.destroy();
          }
          return  Cesium.destroyObject(this);
      }

      //开启动画
      TetrahedronPrimitive.prototype.startAnimate=function(){
        let that=this;
        this._setInterval=setInterval(animateFunc,5);
          function animateFunc(){
              that._angle=that._angle+0.01;
            if(Math.sin(that._angle) < 0)
            {
              that._height=0.01;
            }
            else
            {
              that._height=-0.01;
            }

              let translation =  new Cesium.Cartesian3( 0, 0, that._height );
              Cesium.Matrix4.multiplyByTranslation(that._modelMatrix, translation, that._modelMatrix);
              let rotationZ =  Cesium.Matrix4.fromRotationTranslation( Cesium.Matrix3.fromRotationZ(Cesium.Math.toRadians(that._speed)));
              Cesium.Matrix4.multiply(that._modelMatrix, rotationZ, that._modelMatrix);
          }
      }
      //关闭动画
      TetrahedronPrimitive.prototype.closeAnimate=function(){
          clearInterval(this._setInterval);
      }
      //创建command
      function createCommand(context,tetrahedronPrimitive) {
          var translucent = false;
          var closed = true;
          var vs = creaateVertexShader();
          var fs = createFragmentShader();
          // 借用一下Appearance.getDefaultRenderState
          var rawRenderState =  Cesium.Appearance.getDefaultRenderState(translucent, closed, undefined);
          var renderState =  Cesium.RenderState.fromCache(rawRenderState);
          var vertexShaderSource = new  Cesium.ShaderSource({
              sources: [vs]
          });
          var fragmentShaderSource = new  Cesium.ShaderSource({
              sources: [fs]
          });
          var uniformMap = {
              color: function() {
                  return tetrahedronPrimitive._color;
              },
              myImage: function() {
                  if (Cesium.defined(tetrahedronPrimitive._texture)) {
                      return tetrahedronPrimitive._texture;
                  } else {
                      return tetrahedronPrimitive._viewer.scene.context.defaultTexture;
                  }
              }
          };
          let attributeLocations = {
              position: 0,
              textureCoordinates:1
          };
          var shaderProgram =  Cesium.ShaderProgram.fromCache({
              context: context,
              vertexShaderSource: vertexShaderSource,
              fragmentShaderSource: fragmentShaderSource,
              attributeLocations: attributeLocations
          });
          return new  Cesium.DrawCommand({
              vertexArray: createVertexArray(context),
              primitiveType:  Cesium.PrimitiveType.TRIANGLES,
              renderState: renderState,
              shaderProgram: shaderProgram,
              uniformMap: uniformMap,
              owner: this,
              pass:  Cesium.Pass.TRANSLUCENT,
              modelMatrix: tetrahedronPrimitive._modelMatrix,
          });
      }
      //创建vertexArray
      function createVertexArray(context) {
          let attributeLocations = {
              position: 0,
              textureCoordinates:1
          };
          var positionsAndIndice=cereatePositionsAndIndice();
          var geometry = new  Cesium.Geometry({
              attributes: {
                  position: new  Cesium.GeometryAttribute({
                      // 使用double类型的position进行计算
                      // componentDatatype : Cesium.ComponentDatatype.DOUBLE,
                      componentDatatype:  Cesium.ComponentDatatype.FLOAT,
                      componentsPerAttribute: 3,
                      values: positionsAndIndice.positions
                  }),
                  textureCoordinates: new  Cesium.GeometryAttribute({
                      componentDatatype:  Cesium.ComponentDatatype.FLOAT,
                      componentsPerAttribute: 2,
                      values: positionsAndIndice.sts
                  }),
              },
              // Workaround Internet Explorer 11.0.8 lack of TRIANGLE_FAN
              indices: positionsAndIndice.indices,
              primitiveType:  Cesium.PrimitiveType.TRIANGLES,
              boundingSphere:  Cesium.BoundingSphere.fromVertices(positionsAndIndice.positions)
          });
          //计算geometry的法向量
          var geometryNormal=  Cesium.GeometryPipeline.computeNormal(geometry);
          var vertexArray =  Cesium.VertexArray.fromGeometry({
              context: context,
              geometry: geometryNormal,
              attributeLocations: attributeLocations,
              bufferUsage:  Cesium.BufferUsage.STATIC_DRAW,
          });
          return vertexArray;
      }
      //创建顶点数组与索引
      function cereatePositionsAndIndice(){
          var positions = new Float64Array(5 * 3);
          // position 0
          positions[0] = 0.0;
          positions[1] = 1.0;
          positions[2] = 0.0;

          // position 1
          positions[3] = -1.0;
          positions[4] = 0.0;
          positions[5] = 0.0;

          // position 2
          positions[6] = 0.0;
          positions[7] = -1.0;
          positions[8] = 0.0;

          // position 3
          positions[9] = 1.0;
          positions[10] = 0.0;
          positions[11] = 0.0;

          // position 4
          positions[12] = 0.0;
          positions[13] = 0.0;
          positions[14] = 1.0;
          var indices = new Uint16Array(6 * 3);
          // back triangle
          indices[0] = 4;
          indices[1] = 2;
          indices[2] = 3;

          // left triangle
          indices[3] = 4;
          indices[4] = 3;
          indices[5] = 0;

          // right triangle
          indices[6] = 4;
          indices[7] = 0;
          indices[8] = 1;

          // bottom triangle
          indices[9] = 4;
          indices[10] = 1;
          indices[11] = 2;
          // bottom triangle
          indices[12] = 1;
          indices[13] = 2;
          indices[14] = 3;

          // bottom triangle
          indices[15] = 1;
          indices[16] = 3;
          indices[17] = 0;

          // 1.3 定义纹理数组
        var sts = new Float32Array([
              0.0, 0.0, 1.0, 0.0, 1.0, 1.0,
              0.0, 1.0, 0.5, 0.5,
          ]);
        return {
          indices:indices,
          positions:positions,
          sts:sts
        };
      }
      //创建顶点着色器
      function creaateVertexShader(){
          var vertexShader =
          `
          attribute vec3 position;
          attribute vec3 normal;
          attribute vec2 st;
          attribute float batchId;
          varying vec3 v_positionEC;
          varying vec3 v_normalEC;
          varying vec2 v_st;
          varying vec4 v_pickColor;
          void main()
          {
              v_positionEC = (czm_modelView * vec4(position, 1.0)).xyz;       // position in eye coordinates
              v_normalEC = czm_normal * normal;                               // normal in eye coordinates
              v_st = st;
              //v_pickColor = czm_batchTable_pickColor(batchId);
              gl_Position = czm_modelViewProjection * vec4(position, 1.0);
          }
          `;
          return vertexShader;
      }
      //创建片源着色器
      function createFragmentShader(){
          var fragmentShader =
          `
          varying vec3 v_positionEC;
          varying vec3 v_normalEC;
          varying vec2 v_st;
          uniform vec4 color;
          varying vec4 v_pickColor;
          uniform sampler2D myImage;
          void main()
          {
              vec3 positionToEyeEC = -v_positionEC;
              vec3 normalEC = normalize(v_normalEC);
          #ifdef FACE_FORWARD
              normalEC = faceforward(normalEC, vec3(0.0, 0.0, 1.0), -normalEC);
          #endif
              czm_materialInput materialInput;
              materialInput.normalEC = normalEC;
              materialInput.positionToEyeEC = positionToEyeEC;
              materialInput.st = v_st;
              vec2 st = materialInput.st;
              czm_material material = czm_getDefaultMaterial(materialInput);
              // float dt_a11 = fract(czm_frameNumber / 100.0) * 3.14159265 * 2.0;
              // float dt_a12 = sin(dt_a11);
              // float vst=smoothstep(0.7, 1.0, dt_a12)+0.4;
              // vec4 colorImage = texture2D(myImage, vec2(fract(st.s- czm_frameNumber*0.003), st.t));
              // material.alpha =mix(0.1,1.0,clamp((1.0-st.t) * color.a,0.0,1.0)) +(1.0-sign(st.t-czm_frameNumber*0.001))*0.2*(1.0-colorImage.r)+0.4 ;
              // material.diffuse =(1.0-colorImage.a)*vec3(1.0,0.0,0.0)+colorImage.rgb*vec3(1.0,1.0,0);

              material.alpha = (mix(0.1, 1.0, clamp((1.0 - st.t) * color.a, 0.0, 1.0)) + (1.0 - sign(st.t - czm_frameNumber * 0.001)) * 0.2 + 0.4) * 0.8;
              material.diffuse = color.rgb;
          #ifdef FLAT
              gl_FragColor = vec4(material.diffuse + material.emission, material.alpha);
          #else
              gl_FragColor = czm_phong(normalize(positionToEyeEC), material, czm_lightDirectionEC);
          #endif
          }
          `;
          return fragmentShader;
      }
      //创建纹理
      function createTexture(tetrahedronPrimitive){
          Cesium.Resource.createIfNeeded(tetrahedronPrimitive._imageUrl).fetchImage().then(function(image){
              var vTexture;
              var context = tetrahedronPrimitive._viewer.scene.context;
          if ( Cesium.defined(image.internalFormat)) {
              vTexture = new  Cesium.Texture({
                  context: context,
                  pixelFormat: image.internalFormat,
                  width: image.naturalWidth,
                  height: image.naturalHeight,
                  source: {
                      arrayBufferView: image.bufferView
                  }
              });
          } else {
              vTexture = new  Cesium.Texture({
                  context: context,
                  source: image
              });
          }
          tetrahedronPrimitive._texture = vTexture;
          });
      }
      //计算矩阵
      function computeModelMatrix(tetrahedronPrimitive){
          let enuMatrix =  Cesium.Transforms.eastNorthUpToFixedFrame(tetrahedronPrimitive._localPosition);
          let scaleMatrix =  Cesium.Matrix4.fromScale(tetrahedronPrimitive._scale);
          let modelMatrix =  Cesium.Matrix4.multiply(enuMatrix, scaleMatrix, new  Cesium.Matrix4());
          tetrahedronPrimitive._scaleMatrix=scaleMatrix;
          tetrahedronPrimitive._enuMatrix=enuMatrix;
          return modelMatrix;
      }
      //计算高度
      function computeHeight(tetrahedronPrimitive){
          let point= Cesium.Cartesian3.fromElements(0,0,tetrahedronPrimitive._distance,new Cesium.Cartesian3());
          let enuPoint = Cesium.Matrix4.multiplyByPoint(tetrahedronPrimitive._enuMatrix, point, new  Cesium.Cartesian3());
          let upPositionEC =  Cesium.Matrix4.multiplyByPoint(tetrahedronPrimitive._viewer.scene.camera._viewMatrix, enuPoint, new  Cesium.Cartesian3());
          let upPositionPC =  Cesium.Matrix4.multiplyByPoint(tetrahedronPrimitive._viewer.scene.camera.frustum.projectionMatrix, upPositionEC, new  Cesium.Cartesian3());
          return  Cesium.Cartesian3.normalize(upPositionPC, new  Cesium.Cartesian3()).z;
      }


      // const center = Cesium.Cartesian3.fromDegrees(0.0, 0.0);
        let positions =
        {
          "x": -2481464.959108353,
          "y": 4823824.427904163,
          "z": 3343877.308220879
        };
        
        let colors = [
          new Cesium.Color(1.0,1.0,0.0,1.0),
          new Cesium.Color(1.0,0.0,0.0,0.8),
          new Cesium.Color(0.0,1.0,0.0,0.8),
          new Cesium.Color(0.0,0.0,1.0,0.8),
          new Cesium.Color(0.8, 0.8, 0x0, 0.8)
        ];
        for(let i=0;i<colors.length;i++)
        {
          let pt = new Cesium.Cartesian3(positions.x + i+100, positions.y , positions.z+ i * 100)
          let primitive = new TetrahedronPrimitive({
            position: pt,
            color: colors[i] 
          })
          viewer.scene.primitives.add(primitive);
          primitive.startAnimate();
          if(i === 2)
            viewer.flyTo(primitive);
        }
        
        viewer.camera.setView({
            destination: new Cesium.Cartesian3(
              positions.x, positions.y, positions.z
            ),
            // orientation: {
            //     heading: 6.276226863836136,
            //     pitch: -1.331128445292896,
            //     roll: 0.0001241421687643296
            // }
        });
      }

      function removeSinglePrimitive()
      {
        const primitives = viewer.scene.primitives;
        const length = primitives.length;
        for (let i = 0; i < length; ++i) {
          const p = primitives.get(i);
          // p.show = !p.show;
          if(i === length -1)
            viewer.scene.primitives.remove(p);
        }
      }
  </script>
</body>
</html>

3.2.2 展示结果

4、总结

        总的来说,第一种实现思路方法简单,但需要找到现有的符合自己需求的模型,之后设置旋转和上下浮动效果即可;而第二种实现思路方法更接近底层,需要深入思考,充分理解顶点(Vertex)、三角面(Triangle)、纹理(Texture)、着色器语言(Shaders)、帧动态更新等理论思想,学习Cesium源码是一件极为有趣和快乐的事情,当然需要一定的专业知识背景和先验知识professional knowledge background and prior knowledge)做支撑才能学以致用,深刻领会其精髓。

        正四棱锥(金字塔)结构稳定,拥有坚实的基底,在生物学的食物链、古埃及的墓穴

        尽管并不是每一颗金子都能够闪闪发光,但被周围的沙子所环绕更是一种历练,逆境生长方显生命力之顽强,牢记自己的独特与唯一,坦然面对周围事物,刻在骨子里的坚强终有一日会在芸芸众生中闪耀光芒。愿大家珍惜眼前的一切,以顽强拼搏的奋斗姿态期待和迎接更加光明和美好的未来。
        祝大家国庆节快乐,祖国繁荣昌盛,人民幸福安康!

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值