Scene中的render方法

nmj2086 / 2023-05-05 / 原文

Scene翻译成中文是场景的意思:

/**
 * Update and render the scene. It is usually not necessary to call this function
 * directly because {@link CesiumWidget} or {@link Viewer} do it automatically.
 * @param {JulianDate} [time] The simulation time at which to render.
 */
Scene.prototype.render = function (time) {
  /**
   *
   * Pre passes update. Execute any pass invariant code that should run before the passes here.
   *
   */
  this._preUpdate.raiseEvent(this, time);

  var frameState = this._frameState;
  frameState.newFrame = false;

  if (!defined(time)) {
    time = JulianDate.now();
  }

  // Determine if shouldRender
  var cameraChanged = this._view.checkForCameraUpdates(this);
  var shouldRender =
    !this.requestRenderMode ||
    this._renderRequested ||
    cameraChanged ||
    this._logDepthBufferDirty ||
    this._hdrDirty ||
    this.mode === SceneMode.MORPHING;
  if (
    !shouldRender &&
    defined(this.maximumRenderTimeChange) &&
    defined(this._lastRenderTime)
  ) {
    var difference = Math.abs(
      JulianDate.secondsDifference(this._lastRenderTime, time)
    );
    shouldRender = shouldRender || difference > this.maximumRenderTimeChange;
  }

  if (shouldRender) {
    this._lastRenderTime = JulianDate.clone(time, this._lastRenderTime);
    this._renderRequested = false;
    this._logDepthBufferDirty = false;
    this._hdrDirty = false;

    var frameNumber = CesiumMath.incrementWrap(
      frameState.frameNumber,
      15000000.0,
      1.0
    );
    updateFrameNumber(this, frameNumber, time);
    frameState.newFrame = true;
  }

  tryAndCatchError(this, prePassesUpdate);

  /**
   *
   * Passes update. Add any passes here
   *
   */
  if (this.primitives.show) {
    tryAndCatchError(this, updateMostDetailedRayPicks);
    tryAndCatchError(this, updatePreloadPass);
    tryAndCatchError(this, updatePreloadFlightPass);
    if (!shouldRender) {
      tryAndCatchError(this, updateRequestRenderModeDeferCheckPass);
    }
  }

  this._postUpdate.raiseEvent(this, time);

  if (shouldRender) {
    this._preRender.raiseEvent(this, time);
    frameState.creditDisplay.beginFrame();
    tryAndCatchError(this, render);
  }

  /**
   *
   * Post passes update. Execute any pass invariant code that should run after the passes here.
   *
   */
  updateDebugShowFramesPerSecond(this, shouldRender);
  tryAndCatchError(this, postPassesUpdate);

  // Often used to trigger events (so don't want in trycatch) that the user might be subscribed to. Things like the tile load events, ready promises, etc.
  // We don't want those events to resolve during the render loop because the events might add new primitives
  callAfterRenderFunctions(this);

  if (shouldRender) {
    this._postRender.raiseEvent(this, time);
    frameState.creditDisplay.endFrame();
  }
};

看完是不是一头雾水。。渲染管线里面那么多步骤去哪了?传值在哪里?输出结果在哪里?怎么控制场景的投影转换到view?

不如先看一下Scene有哪些参数和方法构成吧。

function Scene(options) {
  options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  var canvas = options.canvas;
  var creditContainer = options.creditContainer;
  var creditViewport = options.creditViewport;

  var contextOptions = clone(options.contextOptions);
  if (!defined(contextOptions)) {
    contextOptions = {};
  }
  if (!defined(contextOptions.webgl)) {
    contextOptions.webgl = {};
  }
  contextOptions.webgl.powerPreference = defaultValue(
    contextOptions.webgl.powerPreference,
    "high-performance"
  );

  //>>includeStart('debug', pragmas.debug);
  if (!defined(canvas)) {
    throw new DeveloperError("options and options.canvas are required.");
  }
  //>>includeEnd('debug');
  var hasCreditContainer = defined(creditContainer);
  var context = new Context(canvas, contextOptions);
  if (!hasCreditContainer) {
    creditContainer = document.createElement("div");
    creditContainer.style.position = "absolute";
    creditContainer.style.bottom = "0";
    creditContainer.style["text-shadow"] = "0 0 2px #000000";
    creditContainer.style.color = "#ffffff";
    creditContainer.style["font-size"] = "10px";
    creditContainer.style["padding-right"] = "5px";
    canvas.parentNode.appendChild(creditContainer);
  }
  if (!defined(creditViewport)) {
    creditViewport = canvas.parentNode;
  }

  this._id = createGuid();
  this._jobScheduler = new JobScheduler();
  this._frameState = new FrameState(
    context,
    new CreditDisplay(creditContainer, " • ", creditViewport),
    this._jobScheduler
  );
  this._frameState.scene3DOnly = defaultValue(options.scene3DOnly, false);
  this._removeCreditContainer = !hasCreditContainer;
  this._creditContainer = creditContainer;

  this._canvas = canvas;
  this._context = context;
  this._computeEngine = new ComputeEngine(context);
  this._globe = undefined;
  this._globeTranslucencyState = new GlobeTranslucencyState();
  this._primitives = new PrimitiveCollection();
  this._groundPrimitives = new PrimitiveCollection();

  this._globeHeight = undefined;
  this._cameraUnderground = false;

  this._logDepthBuffer = context.fragmentDepth;
  this._logDepthBufferDirty = true;

  this._tweens = new TweenCollection();

  this._shaderFrameCount = 0;

  this._sunPostProcess = undefined;

  this._computeCommandList = [];
  this._overlayCommandList = [];

  this._useOIT = defaultValue(options.orderIndependentTranslucency, true);
  this._executeOITFunction = undefined;

  this._depthPlane = new DepthPlane();

  this._clearColorCommand = new ClearCommand({
    color: new Color(),
    stencil: 0,
    owner: this,
  });
  this._depthClearCommand = new ClearCommand({
    depth: 1.0,
    owner: this,
  });
  this._stencilClearCommand = new ClearCommand({
    stencil: 0,
  });
  this._classificationStencilClearCommand = new ClearCommand({
    stencil: 0,
    renderState: RenderState.fromCache({
      stencilMask: StencilConstants.CLASSIFICATION_MASK,
    }),
  });

  this._depthOnlyRenderStateCache = {};

  this._transitioner = new SceneTransitioner(this);

  this._preUpdate = new Event();
  this._postUpdate = new Event();

  this._renderError = new Event();
  this._preRender = new Event();
  this._postRender = new Event();

  this._minimumDisableDepthTestDistance = 0.0;
  this._debugInspector = new DebugInspector();

  /**
   * Exceptions occurring in <code>render</code> are always caught in order to raise the
   * <code>renderError</code> event.  If this property is true, the error is rethrown
   * after the event is raised.  If this property is false, the <code>render</code> function
   * returns normally after raising the event.
   *
   * @type {Boolean}
   * @default false
   */
  this.rethrowRenderErrors = false;

  /**
   * Determines whether or not to instantly complete the
   * scene transition animation on user input.
   *
   * @type {Boolean}
   * @default true
   */
  this.completeMorphOnUserInput = true;

  /**
   * The event fired at the beginning of a scene transition.
   * @type {Event}
   * @default Event()
   */
  this.morphStart = new Event();

  /**
   * The event fired at the completion of a scene transition.
   * @type {Event}
   * @default Event()
   */
  this.morphComplete = new Event();

  /**
   * The {@link SkyBox} used to draw the stars.
   *
   * @type {SkyBox}
   * @default undefined
   *
   * @see Scene#backgroundColor
   */
  this.skyBox = undefined;

  /**
   * The sky atmosphere drawn around the globe.
   *
   * @type {SkyAtmosphere}
   * @default undefined
   */
  this.skyAtmosphere = undefined;

  /**
   * The {@link Sun}.
   *
   * @type {Sun}
   * @default undefined
   */
  this.sun = undefined;

  /**
   * Uses a bloom filter on the sun when enabled.
   *
   * @type {Boolean}
   * @default true
   */
  this.sunBloom = true;
  this._sunBloom = undefined;

  /**
   * The {@link Moon}
   *
   * @type Moon
   * @default undefined
   */
  this.moon = undefined;

  /**
   * The background color, which is only visible if there is no sky box, i.e., {@link Scene#skyBox} is undefined.
   *
   * @type {Color}
   * @default {@link Color.BLACK}
   *
   * @see Scene#skyBox
   */
  this.backgroundColor = Color.clone(Color.BLACK);

  this._mode = SceneMode.SCENE3D;

  this._mapProjection = defined(options.mapProjection)
    ? options.mapProjection
    : new GeographicProjection();

  /**
   * The current morph transition time between 2D/Columbus View and 3D,
   * with 0.0 being 2D or Columbus View and 1.0 being 3D.
   *
   * @type {Number}
   * @default 1.0
   */
  this.morphTime = 1.0;

  /**
   * The far-to-near ratio of the multi-frustum when using a normal depth buffer.
   * <p>
   * This value is used to create the near and far values for each frustum of the multi-frustum. It is only used
   * when {@link Scene#logarithmicDepthBuffer} is <code>false</code>. When <code>logarithmicDepthBuffer</code> is
   * <code>true</code>, use {@link Scene#logarithmicDepthFarToNearRatio}.
   * </p>
   *
   * @type {Number}
   * @default 1000.0
   */
  this.farToNearRatio = 1000.0;

  /**
   * The far-to-near ratio of the multi-frustum when using a logarithmic depth buffer.
   * <p>
   * This value is used to create the near and far values for each frustum of the multi-frustum. It is only used
   * when {@link Scene#logarithmicDepthBuffer} is <code>true</code>. When <code>logarithmicDepthBuffer</code> is
   * <code>false</code>, use {@link Scene#farToNearRatio}.
   * </p>
   *
   * @type {Number}
   * @default 1e9
   */
  this.logarithmicDepthFarToNearRatio = 1e9;

  /**
   * Determines the uniform depth size in meters of each frustum of the multifrustum in 2D. If a primitive or model close
   * to the surface shows z-fighting, decreasing this will eliminate the artifact, but decrease performance. On the
   * other hand, increasing this will increase performance but may cause z-fighting among primitives close to the surface.
   *
   * @type {Number}
   * @default 1.75e6
   */
  this.nearToFarDistance2D = 1.75e6;

  /**
   * This property is for debugging only; it is not for production use.
   * <p>
   * A function that determines what commands are executed.  As shown in the examples below,
   * the function receives the command's <code>owner</code> as an argument, and returns a boolean indicating if the
   * command should be executed.
   * </p>
   * <p>
   * The default is <code>undefined</code>, indicating that all commands are executed.
   * </p>
   *
   * @type Function
   *
   * @default undefined
   *
   * @example
   * // Do not execute any commands.
   * scene.debugCommandFilter = function(command) {
   *     return false;
   * };
   *
   * // Execute only the billboard's commands.  That is, only draw the billboard.
   * var billboards = new Cesium.BillboardCollection();
   * scene.debugCommandFilter = function(command) {
   *     return command.owner === billboards;
   * };
   */
  this.debugCommandFilter = undefined;

  /**
   * This property is for debugging only; it is not for production use.
   * <p>
   * When <code>true</code>, commands are randomly shaded.  This is useful
   * for performance analysis to see what parts of a scene or model are
   * command-dense and could benefit from batching.
   * </p>
   *
   * @type Boolean
   *
   * @default false
   */
  this.debugShowCommands = false;

  /**
   * This property is for debugging only; it is not for production use.
   * <p>
   * When <code>true</code>, commands are shaded based on the frustums they
   * overlap.  Commands in the closest frustum are tinted red, commands in
   * the next closest are green, and commands in the farthest frustum are
   * blue.  If a command overlaps more than one frustum, the color components
   * are combined, e.g., a command overlapping the first two frustums is tinted
   * yellow.
   * </p>
   *
   * @type Boolean
   *
   * @default false
   */
  this.debugShowFrustums = false;

  /**
   * This property is for debugging only; it is not for production use.
   * <p>
   * Displays frames per second and time between frames.
   * </p>
   *
   * @type Boolean
   *
   * @default false
   */
  this.debugShowFramesPerSecond = false;

  /**
   * This property is for debugging only; it is not for production use.
   * <p>
   * Displays depth information for the indicated frustum.
   * </p>
   *
   * @type Boolean
   *
   * @default false
   */
  this.debugShowGlobeDepth = false;

  /**
   * This property is for debugging only; it is not for production use.
   * <p>
   * Indicates which frustum will have depth information displayed.
   * </p>
   *
   * @type Number
   *
   * @default 1
   */
  this.debugShowDepthFrustum = 1;

  /**
   * This property is for debugging only; it is not for production use.
   * <p>
   * When <code>true</code>, draws outlines to show the boundaries of the camera frustums
   * </p>
   *
   * @type Boolean
   *
   * @default false
   */
  this.debugShowFrustumPlanes = false;
  this._debugShowFrustumPlanes = false;
  this._debugFrustumPlanes = undefined;

  /**
   * When <code>true</code>, enables picking using the depth buffer.
   *
   * @type Boolean
   * @default true
   */
  this.useDepthPicking = true;

  /**
   * When <code>true</code>, enables picking translucent geometry using the depth buffer. Note that {@link Scene#useDepthPicking} must also be true for enabling this to work.
   *
   * <p>
   * Render must be called between picks.
   * <br>There is a decrease in performance when enabled. There are extra draw calls to write depth for
   * translucent geometry.
   * </p>
   *
   * @example
   * // picking the position of a translucent primitive
   * viewer.screenSpaceEventHandler.setInputAction(function onLeftClick(movement) {
   *      var pickedFeature = viewer.scene.pick(movement.position);
   *      if (!Cesium.defined(pickedFeature)) {
   *          // nothing picked
   *          return;
   *      }
   *      viewer.scene.render();
   *      var worldPosition = viewer.scene.pickPosition(movement.position);
   * }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
   *
   * @type {Boolean}
   * @default false
   */
  this.pickTranslucentDepth = false;

  /**
   * The time in milliseconds to wait before checking if the camera has not moved and fire the cameraMoveEnd event.
   * @type {Number}
   * @default 500.0
   * @private
   */
  this.cameraEventWaitTime = 500.0;

  /**
   * Blends the atmosphere to geometry far from the camera for horizon views. Allows for additional
   * performance improvements by rendering less geometry and dispatching less terrain requests.
   * @type {Fog}
   */
  this.fog = new Fog();

  this._shadowMapCamera = new Camera(this);

  /**
   * The shadow map for the scene's light source. When enabled, models, primitives, and the globe may cast and receive shadows.
   * @type {ShadowMap}
   */
  this.shadowMap = new ShadowMap({
    context: context,
    lightCamera: this._shadowMapCamera,
    enabled: defaultValue(options.shadows, false),
  });

  /**
   * When <code>false</code>, 3D Tiles will render normally. When <code>true</code>, classified 3D Tile geometry will render normally and
   * unclassified 3D Tile geometry will render with the color multiplied by {@link Scene#invertClassificationColor}.
   * @type {Boolean}
   * @default false
   */
  this.invertClassification = false;

  /**
   * The highlight color of unclassified 3D Tile geometry when {@link Scene#invertClassification} is <code>true</code>.
   * <p>When the color's alpha is less than 1.0, the unclassified portions of the 3D Tiles will not blend correctly with the classified positions of the 3D Tiles.</p>
   * <p>Also, when the color's alpha is less than 1.0, the WEBGL_depth_texture and EXT_frag_depth WebGL extensions must be supported.</p>
   * @type {Color}
   * @default Color.WHITE
   */
  this.invertClassificationColor = Color.clone(Color.WHITE);

  this._actualInvertClassificationColor = Color.clone(
    this._invertClassificationColor
  );
  this._invertClassification = new InvertClassification();

  /**
   * The focal length for use when with cardboard or WebVR.
   * @type {Number}
   */
  this.focalLength = undefined;

  /**
   * The eye separation distance in meters for use with cardboard or WebVR.
   * @type {Number}
   */
  this.eyeSeparation = undefined;

  /**
   * Post processing effects applied to the final render.
   * @type {PostProcessStageCollection}
   */
  this.postProcessStages = new PostProcessStageCollection();

  this._brdfLutGenerator = new BrdfLutGenerator();

  this._terrainExaggeration = defaultValue(options.terrainExaggeration, 1.0);

  this._performanceDisplay = undefined;
  this._debugVolume = undefined;

  this._screenSpaceCameraController = new ScreenSpaceCameraController(this);
  this._cameraUnderground = false;
  this._mapMode2D = defaultValue(options.mapMode2D, MapMode2D.INFINITE_SCROLL);

  // Keeps track of the state of a frame. FrameState is the state across
  // the primitives of the scene. This state is for internally keeping track
  // of celestial and environment effects that need to be updated/rendered in
  // a certain order as well as updating/tracking framebuffer usage.
  this._environmentState = {
    skyBoxCommand: undefined,
    skyAtmosphereCommand: undefined,
    sunDrawCommand: undefined,
    sunComputeCommand: undefined,
    moonCommand: undefined,

    isSunVisible: false,
    isMoonVisible: false,
    isReadyForAtmosphere: false,
    isSkyAtmosphereVisible: false,

    clearGlobeDepth: false,
    useDepthPlane: false,
    renderTranslucentDepthForPick: false,

    originalFramebuffer: undefined,
    useGlobeDepthFramebuffer: false,
    separatePrimitiveFramebuffer: false,
    useOIT: false,
    useInvertClassification: false,
    usePostProcess: false,
    usePostProcessSelected: false,
    useWebVR: false,
  };

  this._useWebVR = false;
  this._cameraVR = undefined;
  this._aspectRatioVR = undefined;

  /**
   * When <code>true</code>, rendering a frame will only occur when needed as determined by changes within the scene.
   * Enabling improves performance of the application, but requires using {@link Scene#requestRender}
   * to render a new frame explicitly in this mode. This will be necessary in many cases after making changes
   * to the scene in other parts of the API.
   *
   * @see {@link https://cesium.com/blog/2018/01/24/cesium-scene-rendering-performance/|Improving Performance with Explicit Rendering}
   * @see Scene#maximumRenderTimeChange
   * @see Scene#requestRender
   *
   * @type {Boolean}
   * @default false
   */
  this.requestRenderMode = defaultValue(options.requestRenderMode, false);
  this._renderRequested = true;

  /**
   * If {@link Scene#requestRenderMode} is <code>true</code>, this value defines the maximum change in
   * simulation time allowed before a render is requested. Lower values increase the number of frames rendered
   * and higher values decrease the number of frames rendered. If <code>undefined</code>, changes to
   * the simulation time will never request a render.
   * This value impacts the rate of rendering for changes in the scene like lighting, entity property updates,
   * and animations.
   *
   * @see {@link https://cesium.com/blog/2018/01/24/cesium-scene-rendering-performance/|Improving Performance with Explicit Rendering}
   * @see Scene#requestRenderMode
   *
   * @type {Number}
   * @default 0.0
   */
  this.maximumRenderTimeChange = defaultValue(
    options.maximumRenderTimeChange,
    0.0
  );
  this._lastRenderTime = undefined;
  this._frameRateMonitor = undefined;

  this._removeRequestListenerCallback = RequestScheduler.requestCompletedEvent.addEventListener(
    requestRenderAfterFrame(this)
  );
  this._removeTaskProcessorListenerCallback = TaskProcessor.taskCompletedEvent.addEventListener(
    requestRenderAfterFrame(this)
  );
  this._removeGlobeCallbacks = [];

  var viewport = new BoundingRectangle(
    0,
    0,
    context.drawingBufferWidth,
    context.drawingBufferHeight
  );
  var camera = new Camera(this);

  if (this._logDepthBuffer) {
    camera.frustum.near = 0.1;
    camera.frustum.far = 10000000000.0;
  }

  /**
   * The camera view for the scene camera flight destination. Used for preloading flight destination tiles.
   * @type {Camera}
   * @private
   */
  this.preloadFlightCamera = new Camera(this);

  /**
   * The culling volume for the scene camera flight destination. Used for preloading flight destination tiles.
   * @type {CullingVolume}
   * @private
   */
  this.preloadFlightCullingVolume = undefined;

  this._picking = new Picking(this);
  this._defaultView = new View(this, camera, viewport);
  this._view = this._defaultView;

  this._hdr = undefined;
  this._hdrDirty = undefined;
  this.highDynamicRange = false;
  this.gamma = 2.2;

  /**
   * The spherical harmonic coefficients for image-based lighting of PBR models.
   * @type {Cartesian3[]}
   */
  this.sphericalHarmonicCoefficients = undefined;

  /**
   * The url to the KTX file containing the specular environment map and convoluted mipmaps for image-based lighting of PBR models.
   * @type {String}
   */
  this.specularEnvironmentMaps = undefined;
  this._specularEnvironmentMapAtlas = undefined;

  /**
   * The light source for shading. Defaults to a directional light from the Sun.
   * @type {Light}
   */
  this.light = new SunLight();

  // Give frameState, camera, and screen space camera controller initial state before rendering
  updateFrameNumber(this, 0.0, JulianDate.now());
  this.updateFrameState();
  this.initializeFrame();
}