• Cesium渲染模块之Command


    1. 引言

    Cesium是一款三维地球和地图可视化开源JavaScript库,使用WebGL来进行硬件加速图形,使用时不需要任何插件支持,基于Apache2.0许可的开源程序,可以免费用于商业和非商业用途

    Cesium官网:Cesium: The Platform for 3D Geospatial

    Cesium GitHub站点:CesiumGS/cesium: An open-source JavaScript library for world-class 3D globes and maps (github.com)

    API文档:Index - Cesium Documentation

    通过阅读源码,理清代码逻辑,有助于扩展与开发,笔者主要参考了以下两个系列的文章

    渲染是前端可视化的核心,本文描述Cesium渲染模块的Command

    2. Cesium中的Command

    Cesium中的Command对象包含执行的指令参数和执行方法,比如最简单的ClearCommand

    function ClearCommand(options) {
    // ...
    this.color = options.color;
    this.depth = options.depth;
    this.stencil = options.stencil;
    this.renderState = options.renderState;
    this.framebuffer = options.framebuffer;
    this.owner = options.owner;
    this.pass = options.pass;
    }
    ClearCommand.prototype.execute = function (context, passState) {
    context.clear(this, passState);
    };

    ClearCommand包含颜色、深度、通道等指令参数和执行方法context.clear(this, passState)

    Command对象主要有三类:

    • ClearCommand
    • DrawCommand
    • ComputeCommand

    正如其名,ClearCommand用于清除,DrawCommand用于绘制,ComputeCommand用于计算

    3. ClearCommand

    ClearCommand的封装很简单,如上述代码所示:

    function ClearCommand(options) {
    // ...
    this.color = options.color;
    this.depth = options.depth;
    this.stencil = options.stencil;
    this.renderState = options.renderState;
    this.framebuffer = options.framebuffer;
    this.owner = options.owner;
    this.pass = options.pass;
    }
    ClearCommand.prototype.execute = function (context, passState) {
    context.clear(this, passState);
    };

    context.clear()会执行清除的WebGL指令:

    Context.prototype.clear = function (clearCommand, passState) {
    // ...
    const c = clearCommand.color;
    const d = clearCommand.depth;
    const s = clearCommand.stencil;
    gl.clearColor(c.red, c.green, c.blue, c.alpha);
    gl.clearDepth(d);
    gl.clearStencil(s);
    bindFramebuffer(this, framebuffer);
    gl.clear(bitmask);
    };

    ClearCommand在Scene中的调用:

    初始化Scene时初始化ClearCommand

    function Scene(options) {
    // ...
    this._clearColorCommand = new ClearCommand({
    color: new Color(),
    stencil: 0,
    owner: this,
    });
    // ...
    }

    执行更新时调用ClearCommandexecute()方法

    Scene.prototype.updateAndExecuteCommands = function (passState, backgroundColor) {
    // ...
    updateAndClearFramebuffers(this, passState, backgroundColor);
    // ...
    };
    function updateAndClearFramebuffers(scene, passState, clearColor) {
    // ...
    // Clear the pass state framebuffer.
    const clear = scene._clearColorCommand;
    Color.clone(clearColor, clear.color);
    clear.execute(context, passState);
    // ...
    }

    4. DrawCommand

    DrawCommand是最常用的指令,它是绘制的主角

    DrawCommand封装如下,几乎包含了绘制所需要的全部内容:

    function DrawCommand(options) {
    options = defaultValue(options, defaultValue.EMPTY_OBJECT);
    this._boundingVolume = options.boundingVolume;
    this._orientedBoundingBox = options.orientedBoundingBox;
    this._modelMatrix = options.modelMatrix;
    this._primitiveType = defaultValue(
    options.primitiveType,
    PrimitiveType.TRIANGLES
    );
    this._vertexArray = options.vertexArray;
    this._count = options.count;
    this._offset = defaultValue(options.offset, 0);
    this._instanceCount = defaultValue(options.instanceCount, 0);
    this._shaderProgram = options.shaderProgram;
    this._uniformMap = options.uniformMap;
    this._renderState = options.renderState;
    this._framebuffer = options.framebuffer;
    this._pass = options.pass;
    this._owner = options.owner;
    this._debugOverlappingFrustums = 0;
    this._pickId = options.pickId;
    // ...
    }
    DrawCommand.prototype.execute = function (context, passState) {
    context.draw(this, passState);
    };

    context.draw()执行WebGL的绘制指令:

    Context.prototype.draw = function (drawCommand, passState, shaderProgram, uniformMap) {
    // ...
    beginDraw(this, framebuffer, passState, shaderProgram, renderState);
    continueDraw(this, drawCommand, shaderProgram, uniformMap);
    };
    function continueDraw(context, drawCommand, shaderProgram, uniformMap) {
    // ...
    va._bind();
    context._gl.drawArrays(primitiveType, offset, count);
    // ...
    va._unBind();
    }

    DrawCommand在Scene中的调用:

    初始化Scene时初始化PrimitiveCollection

    function Scene(options) {
    // ...
    this._primitives = new PrimitiveCollection();
    this._groundPrimitives = new PrimitiveCollection();
    // ...
    }

    执行更新时调用DrawCommandprimitives.update(frameState)()方法

    Scene.prototype.updateAndExecuteCommands = function (passState, backgroundColor) {
    // ...
    executeCommandsInViewport(true, this, passState, backgroundColor);
    // ...
    };
    function executeCommandsInViewport(firstViewport, scene, passState, backgroundColor) {
    // ...
    updateAndRenderPrimitives(scene);
    // ...
    }
    function updateAndRenderPrimitives(scene) {
    // ...
    scene._groundPrimitives.update(frameState);
    scene._primitives.update(frameState);
    // ...
    }

    再来看看primitives.update(frameState)方法:

    PrimitiveCollection.prototype.update = function (frameState) {
    const primitives = this._primitives;
    for (let i = 0; i < primitives.length; ++i) {
    primitives[i].update(frameState);
    }
    };
    Primitive.prototype.update = function (frameState) {
    // ...
    const updateAndQueueCommandsFunc = updateAndQueueCommands
    updateAndQueueCommandsFunc(...);
    };
    function updateAndQueueCommands(...) {
    // ...
    const commandList = frameState.commandList;
    const passes = frameState.passes;
    if (passes.render || passes.pick) {
    const colorLength = colorCommands.length;
    for (let j = 0; j < colorLength; ++j) {
    const colorCommand = colorCommands[j];
    // ...
    commandList.push(colorCommand);
    }
    }
    }

    primitives.update(frameState)方法会将Command推入CommandList,然后在Scene中执行execute()方法:

    function executeCommands(scene, passState) {
    // ...
    // Draw terrain classification
    executeCommand(commands[j], scene, context, passState);
    // Draw 3D Tiles
    executeCommand(commands[j], scene, context, passState)
    // Draw classifications. Modifies 3D Tiles color.
    executeCommand(commands[j], scene, context, passState);
    // ...
    }
    function executeCommand(command, scene, context, passState, debugFramebuffer) {
    // ...
    command.execute(context, passState);
    // ...
    }

    5. ComputeCommand

    ComputeCommand需要配合ComputeEngine一起使用,可以将它认为是一个特殊的DrawCommand,通过渲染机制实现GPU的计算,通过Shader计算结果保存到纹理传出,实现在Web前端高效的处理大量的数值计算

    ComputeCommand的构造函数如下:

    function ComputeCommand(options) {
    options = defaultValue(options, defaultValue.EMPTY_OBJECT);
    this.vertexArray = options.vertexArray;
    this.fragmentShaderSource = options.fragmentShaderSource;
    this.shaderProgram = options.shaderProgram;
    this.uniformMap = options.uniformMap;
    this.outputTexture = options.outputTexture;
    this.preExecute = options.preExecute;
    this.postExecute = options.postExecute;
    this.canceled = options.canceled;
    this.persists = defaultValue(options.persists, false);
    this.pass = Pass.COMPUTE;
    this.owner = options.owner;
    }
    ComputeCommand.prototype.execute = function (computeEngine) {
    computeEngine.execute(this);
    };

    computeEngine.execute()方法使用DrawCommandClearCommand执行计算:

    ComputeEngine.prototype.execute = function (computeCommand) {
    // ...
    computeCommand.preExecute(computeCommand);
    const outputTexture = computeCommand.outputTexture;
    const framebuffer = createFramebuffer(context, outputTexture);
    // ...
    clearCommand.execute(context);
    drawCommand.framebuffer = framebuffer;
    drawCommand.execute(context);
    framebuffer.destroy();
    computeCommand.postExecute(outputTexture);
    };

    ImageryLayer.js中重投影就使用了ComputeCommand

    ImageryLayer.prototype._reprojectTexture = function (frameState, imagery, needGeographicProjection) {
    // ...
    const computeCommand = new ComputeCommand({
    persists: true,
    owner: this,
    // Update render resources right before execution instead of now.
    // This allows different ImageryLayers to share the same vao and buffers.
    preExecute: function (command) {
    reprojectToGeographic(command, context, texture, imagery.rectangle);
    },
    postExecute: function (outputTexture) {
    imagery.texture = outputTexture;
    that._finalizeReprojectTexture(context, outputTexture);
    imagery.state = ImageryState.READY;
    imagery.releaseReference();
    },
    canceled: function () {
    imagery.state = ImageryState.TEXTURE_LOADED;
    imagery.releaseReference();
    },
    });
    this._reprojectComputeCommands.push(computeCommand);
    // ...
    };

    6. 参考资料

    [1] Cesium原理篇:6 Render模块(5: VAO&RenderState&Command) - fu*k - 博客园 (cnblogs.com)

    [2] Cesium渲染模块之概述 - 当时明月在曾照彩云归 - 博客园 (cnblogs.com)

    [3] Cesium渲染调度 - 当时明月在曾照彩云归 - 博客园 (cnblogs.com)

    [4] CesiumJS 2022^ 源码解读 5 - 着色器相关的封装设计 - 岭南灯火 - 博客园 (cnblogs.com)

    [5] Cesium教程系列汇总 - fu*k - 博客园 (cnblogs.com)

  • 相关阅读:
    [附源码]计算机毕业设计JAVAjsp闲置物品线上交易系统
    工业涂装行业的物联网解决方案
    回溯算法4.1-4.4
    Qt 布局(QLayout 类&QStackedWidget 类) 总结
    Java版工程行业管理系统源码-专业的工程管理软件- 工程项目各模块及其功能点清单
    从零开始:新手快速在国产操作系统中搭建高可用K8S(V1.28)集群落地实践
    【Leetcode】2369. 检查数组是否存在有效划分
    关于微信学习的网站
    河北保定鱼米之乡重现 国稻种芯·中国水稻节:雄安新区稻田
    SpringBoot美食网站系统
  • 原文地址:https://www.cnblogs.com/jiujiubashiyi/p/17216978.html