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);
// ...
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dunming_6725413

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值