7

Cesium渲染模块之Command - 当时明月在曾照彩云归

 1 year ago
source link: https://www.cnblogs.com/jiujiubashiyi/p/17216978.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK