Cesium渲染機制概覽

Cesium基於WebGL實現,所以渲染機制跟我們直接用WebGL的道理相同,但是內部功能強大,會更復雜些
目前不討論Cesium的實現原理,先了解下Cesium的大體渲染流程的機制,有個初步認識

HelloWorld

亙古不變的HelloWorld,一句代碼就可以繪製出來地球和很多小部件

var viewer = new Cesium.Viewer("cesiumContainer"); 

來一層一層看下地球是如何出來的
Viewer.js 相關代碼

    function Viewer(container, options) { 
      container = getElement(container);
      options = defaultValue(options, defaultValue.EMPTY_OBJECT);
      
      var viewerContainer = document.createElement("div");
      viewerContainer.className = "cesium-viewer";
      container.appendChild(viewerContainer);

      // Cesium widget container
      var cesiumWidgetContainer = document.createElement("div"); 
      viewerContainer.appendChild(cesiumWidgetContainer);

      // Bottom container
      var bottomContainer = document.createElement("div");  
      viewerContainer.appendChild(bottomContainer); 

      // Cesium widget
      var cesiumWidget = new CesiumWidget(cesiumWidgetContainer, {
        imageryProvider: imageryProvider,
        // ...
        sceneMode: options.sceneMode, 
      });
           
      // ...
      // 初始化具體小部件、屬性、事件等
    }

Viewer()方法作爲入口,構建必須的dom元素,初始化具體小部件、屬性、事件等,很重要的一個就是實例化了CesiumWidget,而CesiumWidget內部構建了定時器,用來不斷刷新並渲染頁面
CesiumWidget.js 相關代碼

// 定義定時器
    function startRenderLoop(widget) {
      var lastFrameTime = 0;
      function render(frameTime) {
        // ...
        widget.resize();
        widget.render();
        requestAnimationFrame(render);
        // ...
      }
      requestAnimationFrame(render);
    }

    function CesiumWidget(container, options) {
      container = getElement(container);

      //Configure the widget DOM elements
      var element = document.createElement("div");
      element.className = "cesium-widget";
      container.appendChild(element);

      var canvas = document.createElement("canvas");
      canvas.addEventListener("mousedown", blurActiveElement);
      canvas.addEventListener("pointerdown", blurActiveElement);

      element.appendChild(canvas);

      var scene = new Scene({
        canvas: canvas,
        // ...
      });
      this._scene = scene;

      var ellipsoid = defaultValue(
        scene.mapProjection.ellipsoid,
        Ellipsoid.WGS84
      );

      var globe = options.globe;
      if (!defined(globe)) {
        globe = new Globe(ellipsoid);
      }
      if (globe !== false) {
        scene.globe = globe;
        scene.globe.shadows = defaultValue(
          options.terrainShadows,
          ShadowMode.RECEIVE_ONLY
        );
      }

      var skyBox = options.skyBox;
      var skyAtmosphere = options.skyAtmosphere;
      var imageryProvider = createWorldImagery();
      scene.terrainProvider = options.terrainProvider;

      // 給 useDefaultRenderLoop 屬性賦值,觸發了
      this._useDefaultRenderLoop = undefined;
      this.useDefaultRenderLoop = defaultValue(
        options.useDefaultRenderLoop,
        true
      );

    }

    Object.defineProperties(CesiumWidget.prototype, {
      // ...
      // 通過set設置在賦值時,觸發定時器
      useDefaultRenderLoop: {
        get: function () {
          return this._useDefaultRenderLoop;
        },
        set: function (value) {
          if (this._useDefaultRenderLoop !== value) {
            this._useDefaultRenderLoop = value;
            if (value && !this._renderLoopRunning) {
              startRenderLoop(this);
            }
          }
        },
      }
    })

    // render方法
    CesiumWidget.prototype.render = function () {
      if (this._canRender) {
        this._scene.initializeFrame();
        var currentTime = this._clock.tick();
        // 此時調用_scene的渲染方法
        this._scene.render(currentTime);
      } else {
        this._clock.tick();
      }
    };

this._scene.render(currentTime)執行時,會調用scene的渲染方法,來渲染場景中的各類要素
Scene.js 相關代碼

    function render(scene) {
      // ...
      // globe地球渲染開始
      scene.globe.beginFrame(frameState);
      // 其它環境、覆蓋物渲染等
      scene.updateEnvironment();
      scene.updateAndExecuteCommands(passState, backgroundColor);
      scene.resolveFramebuffers(passState);
      executeOverlayCommands(scene, passState);
      // globe地球渲染結束
      scene.globe.endFrame(frameState);

    }

    Scene.prototype.render = function (time) { 
      tryAndCatchError(this, prePassesUpdate); 
      tryAndCatchError(this, updateMostDetailedRayPicks);
      tryAndCatchError(this, updatePreloadPass);
      tryAndCatchError(this, updatePreloadFlightPass);
      // 調用render()
      tryAndCatchError(this, render); 
    };

此處就可以看到globe的渲染方法登場,地球的繪製是通過地形和地圖疊加到橢球體顯示而成
Globe.js 相關代碼

    function Globe(ellipsoid) {
      ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);
      var terrainProvider = new EllipsoidTerrainProvider({
        ellipsoid: ellipsoid,
      });
      var imageryLayerCollection = new ImageryLayerCollection();

      this._ellipsoid = ellipsoid;
      this._imageryLayerCollection = imageryLayerCollection;

      this._surfaceShaderSet = new GlobeSurfaceShaderSet();
      this._material = undefined;

      this._surface = new QuadtreePrimitive({
        tileProvider: new GlobeSurfaceTileProvider({
          terrainProvider: terrainProvider,
          imageryLayers: imageryLayerCollection,
          surfaceShaderSet: this._surfaceShaderSet,
        }),
      });
      // ...
    }

    Globe.prototype.beginFrame = function (frameState) {
      var surface = this._surface;
      var tileProvider = surface.tileProvider;
      var terrainProvider = this.terrainProvider;
      var hasWaterMask = this.showWaterEffect
      // ...
      surface.tileCacheSize = this.tileCacheSize;
      tileProvider.terrainProvider = this.terrainProvider;
      tileProvider.hasWaterMask = hasWaterMask;
      surface.beginFrame(frameState); 
      // ...
    }; 

地球表面包含着地形、切片、水面等,統一由tileProvider處理,通過surface.beginFrame(frameState)執行
由此,能看到各級關係如下:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章