Cesium渲染模块之FBO与RBO
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渲染模块的FBO
2. WebGL中的FBO
帧缓冲对象(Frame Buffer Object)是被推荐用于将数据渲染到纹理对象的扩展,FrameBuffer就像是一个webgl显示容器一样,平时使用gl.drawArrays或者gl.drawElements都是将对象绘制在了默认的窗口中,而当指定一个FrameBuffer为当前窗口时,用这两个方法去绘制,则会将对象绘制于指定的FrameBuffer中
FBO可以理解为存放颜色对象、深度对象、模板对象的指针的容器,在FBO中可以存放纹理对象或者渲染缓冲区对象(RBO)的指针,如下图所示:

一份极简的包含FBO的WebGL绘制代码如下(完整代码见附录):
// 创建并绑定FBO
const fbo = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
// 创建并绑定纹理对象
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, canvas.width, canvas.height, 0, gl.RGB, gl.UNSIGNED_BYTE, null);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
// 使用FBO进行绘制(并不直接显示)
gl.useProgram(frameShaderProgram);
gl.clearColor(0.2, 0.2, 0.2, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
// 解绑FBO
gl.bindTexture(gl.TEXTURE_2D, null);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
// 在默认窗口绘制FBO的内容
gl.useProgram(shaderProgram);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.clearColor(0.2, 0.3, 0.3, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

上图中,绿色矩形和黑色矩形为FBO绘制的内容,笔者将其作为纹理再次绘制显示出来
FBO的使用大致为以下流程:
- 创建并绑定FBO
gl.createFramebuffer()、gl.bindFramebuffer() - 创建并绑定纹理对象
gl.texImage2D()、gl.framebufferTexture2D() - 使用FBO进行绘制
gl.drawArrays() - 解绑FBO
gl.bindFramebuffer(gl.FRAMEBUFFER, null) - 绘制或使用FBO的对象
gl.bindTexture(gl.TEXTURE_2D, texture)
创建并绑定纹理对象时,和一般的创建纹理对象相比,这里的gl.texImage2D()并没有输入数据,除了可以使用gl.framebufferTexture2D()绑定纹理外,还可以使用gl.bindRenderbuffer()绑定渲染缓冲对象RBO,具体的参数可以参考:
- WebGLRenderingContext.bindRenderbuffer() - Web APIs | MDN (mozilla.org)
- WebGLRenderingContext.bindFramebuffer() - Web APIs | MDN (mozilla.org)
使用FBO绘制过后,数据保存在了FBO绑定的对象中(比如,纹理),再次使用这个对象就可以读取之前FBO绘制的结果(比如上述代码中再次使用纹理对象)
更为具体的流程与函数解释可以参考:帧缓冲 - LearnOpenGL CN (learnopengl-cn.github.io)
3. Cesium中的FBO
Cesium源码中,对FBO进行了一些封装:
function Framebuffer(options) {
// ...
this._bind();
if (defined(options.colorTextures)) {
const textures = options.colorTextures;
length = this._colorTextures.length = this._activeColorAttachments.length = textures.length;
for (i = 0; i < length; ++i) {
texture = textures[i];
attachmentEnum = this._gl.COLOR_ATTACHMENT0 + i;
attachTexture(this, attachmentEnum, texture);
this._activeColorAttachments[i] = attachmentEnum;
this._colorTextures[i] = texture;
}
}
if (defined(options.colorRenderbuffers)) {
const renderbuffers = options.colorRenderbuffers;
length = this._colorRenderbuffers.length = this._activeColorAttachments.length = renderbuffers.length;
for (i = 0; i < length; ++i) {
renderbuffer = renderbuffers[i];
attachmentEnum = this._gl.COLOR_ATTACHMENT0 + i;
attachRenderbuffer(this, attachmentEnum, renderbuffer);
this._activeColorAttachments[i] = attachmentEnum;
this._colorRenderbuffers[i] = renderbuffer;
}
}
// ...
this._unBind();
}
创建一个FBO的示例代码:
// Create a framebuffer with color and depth texture attachments.
const width = context.canvas.clientWidth;
const height = context.canvas.clientHeight;
const framebuffer = new Framebuffer({
context : context,
colorTextures : [new Texture({
context : context,
width : width,
height : height,
pixelFormat : PixelFormat.RGBA
})],
depthTexture : new Texture({
context : context,
width : width,
height : height,
pixelFormat : PixelFormat.DEPTH_COMPONENT,
pixelDatatype : PixelDatatype.UNSIGNED_SHORT
})
});
Framebuffer类还封装了以下函数:
function attachTexture(framebuffer, attachment, texture)
function attachRenderbuffer(framebuffer, attachment, renderbuffer)
Framebuffer.prototype._bind = function ()
Framebuffer.prototype._unBind = function ()
Framebuffer.prototype.bindDraw = function ()
Framebuffer.prototype.bindRead = function ()
Framebuffer.prototype._getActiveColorAttachments = function ()
Framebuffer.prototype.getColorTexture = function (index)
Framebuffer.prototype.getColorRenderbuffer = function (index)
Framebuffer.prototype.destroy = function ()
另外,还有FramebufferManager,用来管理和创建Framebuffer:
FramebufferManager.prototype.update = function(context, width, height, numSamples, pixelDatatype, pixelFormat)
FramebufferManager.prototype.getColorTexture = function (index)
FramebufferManager.prototype.setColorTexture = function (texture, index)
FramebufferManager.prototype.getColorRenderbuffer = function (index)
FramebufferManager.prototype.setColorRenderbuffer = function (renderbuffer, index)
// ...
在Cesium源码中,可以看到构建Framebuffer类主要是通过直接new Framebuffer(),例如ComputeEngine.js:
function createFramebuffer(context, outputTexture) {
return new Framebuffer({
context: context,
colorTextures: [outputTexture],
destroyAttachments: false,
});
}
4. Cesium中的RBO
渲染缓冲对象RBO(Renderbuffer Object)可用于存储颜色,深度或模板值,并可用作帧缓冲区对象中的颜色,深度或模板附件
和FBO相比,RBO是具有真正存储内容的缓冲区,由于渲染缓冲区对象是只写的,因此它们通常用作深度和模板附件,因为大多数时候并不需要读取它们的值,只关心深度和模板测试
由于RBO只写的特性,渲染缓冲区不能直接用作GL纹理
RBO、FBO、Texture通常一起使用进行离屏渲染(offscreen-rendering),关系图大致如下所示:

WebGL中,RBO的使用流程一般为:
// 1. 创建并绑定RBO
const renderbuffer = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer);
// 2. 创建并初始化RBO数据存储
gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 256, 256);
// 3. 将RBO与FBO绑定
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, renderbuffer);
Cesium源码中,对RBO进行了一些封装:
function Renderbuffer(options) {
// ...
gl.bindRenderbuffer(gl.RENDERBUFFER, this._renderbuffer);
if (numSamples > 1) {
gl.renderbufferStorageMultisample(gl.RENDERBUFFER, numSamples, format, width, height);
} else {
gl.renderbufferStorage(gl.RENDERBUFFER, format, width, height);
}
gl.bindRenderbuffer(gl.RENDERBUFFER, null);
}
5. 参考资料
[1] WebGLFramebuffer - Web APIs | MDN (mozilla.org)
[2] 帧缓冲 - LearnOpenGL CN (learnopengl-cn.github.io)
[3] Cesium原理篇:6 Render模块(4: FBO) - fu*k - 博客园 (cnblogs.com)
[4] WebGL中图片多级处理(FrameBuffer) - 纸异兽 - 博客园 (cnblogs.com)
[5] CesiumJS 2022^ 源码解读 5 - 着色器相关的封装设计 - 岭南灯火 - 博客园 (cnblogs.com)
[6] Renderbuffer Object - OpenGL Wiki (khronos.org)
[7] OpenGL FrameBuffer Objects,RenderBuffer Objects and Textures - (caolongs.github.io)
[8] WebGLRenderbuffer - Web API 接口参考 | MDN (mozilla.org)
[9] WebGL— FrameBuffer,RenderBuffer,Texture区别_webglframebuffer 是什么_weixin_43787178的博客-CSDN博客
6. 附录
包含FBO的WebGL绘制代码:
<canvas id="canvas"></canvas>
<script>
const vertexSource = `
attribute vec3 aPos;
attribute vec2 aTextureCoord;
varying highp vec2 vTextureCoord;
void main()
{
gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
vTextureCoord = aTextureCoord;
}
`
const fragmentSource = `
varying highp vec2 vTextureCoord;
uniform sampler2D uSampler;
void main()
{
gl_FragColor = texture2D(uSampler, vTextureCoord);
}
`
const frameFragmentSource = `
void main()
{
gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
}
`
const canvas = document.getElementById('canvas');
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
const gl = canvas.getContext('webgl2');
const vertices = new Float32Array([
0.5, -0.5, 0.0,
-0.5, -0.5, 0.0,
0.5, 0.5, 0.0,
-0.5, 0.5, 0.0,
]);
const vbo = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
const vao = gl.createVertexArray();
gl.bindVertexArray(vao);
gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(0)
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexSource);
gl.compileShader(vertexShader);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentSource);
gl.compileShader(fragmentShader);
const frameFragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(frameFragmentShader, frameFragmentSource);
gl.compileShader(frameFragmentShader);
const shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
const frameShaderProgram = gl.createProgram();
gl.attachShader(frameShaderProgram, vertexShader);
gl.attachShader(frameShaderProgram, frameFragmentShader);
gl.linkProgram(frameShaderProgram);
// 加载纹理坐标到GPU
const textureCoordinates = new Float32Array([
1.0, 0.0,
0.0, 0.0,
1.0, 1.0,
0.0, 1.0,
]);
const textureCoordinatesBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, textureCoordinatesBuffer);
gl.bufferData(gl.ARRAY_BUFFER, textureCoordinates, gl.STATIC_DRAW);
// 设置纹理坐标属性
textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord");
gl.enableVertexAttribArray(textureCoordAttribute);
gl.vertexAttribPointer(textureCoordAttribute, 2, gl.FLOAT, false, 0, 0);
// 创建并绑定FBO
const fbo = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
// 创建并绑定纹理对象
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, canvas.width, canvas.height, 0, gl.RGB, gl.UNSIGNED_BYTE, null);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
// 使用FBO进行绘制(并不直接显示)
gl.useProgram(frameShaderProgram);
gl.clearColor(0.2, 0.2, 0.2, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
// 解绑FBO
gl.bindTexture(gl.TEXTURE_2D, null);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
// 在默认窗口绘制FBO的内容
gl.useProgram(shaderProgram);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.clearColor(0.2, 0.3, 0.3, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
</script>
Cesium渲染模块之FBO与RBO的更多相关文章
- Cesium渲染模块之概述
1. 引言 Cesium是一款三维地球和地图可视化开源JavaScript库,使用WebGL来进行硬件加速图形,使用时不需要任何插件支持,基于Apache2.0许可的开源程序,可以免费用于商业和非商业 ...
- Cesium渲染模块之VAO
1. 引言 Cesium是一款三维地球和地图可视化开源JavaScript库,使用WebGL来进行硬件加速图形,使用时不需要任何插件支持,基于Apache2.0许可的开源程序,可以免费用于商业和非商业 ...
- Cesium渲染模块之Buffer
1. 引言 Cesium是一款三维地球和地图可视化开源JavaScript库,使用WebGL来进行硬件加速图形,使用时不需要任何插件支持,基于Apache2.0许可的开源程序,可以免费用于商业和非商业 ...
- Cesium渲染模块之Shader
1. 引言 Cesium是一款三维地球和地图可视化开源JavaScript库,使用WebGL来进行硬件加速图形,使用时不需要任何插件支持,基于Apache2.0许可的开源程序,可以免费用于商业和非商业 ...
- Cesium渲染模块之Command
1. 引言 Cesium是一款三维地球和地图可视化开源JavaScript库,使用WebGL来进行硬件加速图形,使用时不需要任何插件支持,基于Apache2.0许可的开源程序,可以免费用于商业和非商业 ...
- Cesium原理篇:6 Render模块(4: FBO)
Cesium不仅仅提供了FBO,也就是Framebuffer类,而且整个渲染过程都是在FBO中进行的.FBO,中文就是帧缓冲区,通常都属于高级用法,但其实,如果你了解了它的基本原理后,用起来还是很简单 ...
- WebGPU 中消失的 FBO 和 RBO
目录 1 WebGL 中的 FBO 与 RBO 1.1 帧缓冲对象(FramebufferObject) 1.2 颜色附件与深度模板附件的真正载体 1.3 FBO/RBO/WebGLTexture 相 ...
- (原)Unreal渲染模块 管线 - 着色器(1)
@author: 白袍小道 转载悄悄说明下 随缘查看,施主开心就好 说明: 本篇继续Unreal搬山部分的渲染模块的Shader部分, 主要牵扯模块RenderCore, ShaderCore, RH ...
- (原)Unreal渲染模块 管线 - 程序和场景查询
@author: 白袍小道 查看随意,转载随缘 第一部分: 这里主要关心加速算法,和该阶段相关的UE模块的结构和组件的处理. What-HOW-Why-HOW-What(嘿嘿,老规矩) 1.渲 ...
- (原)Unreal渲染模块 源码和实例分析说明
@author:白袍小道 说明 1.由于小道就三境武夫而已,而UE渲染部分不仅管理挺大,而且牵扯技术和内容驳杂,所以才有这篇梳理. 2.尽量会按书籍和资料,源码,小模块的调试和搬山(就是敲键盘)..等 ...
随机推荐
- JS Leetcode 503. 下一个更大元素 II 题解分析,依旧单调栈做法解决此题
壹 ❀ 引 我在JS Leetcode 496. 下一个更大元素 I 更清晰的图解单调栈做法一文中,介绍了单调栈做法解决下一个更大元素的问题,比较巧的是这道题还有升级版,题目来自Leetcode503 ...
- updatedb命令
updatedb命令 updatedb创建或更新locate命令使用的数据库,如果数据库已经存在,则重用其数据以避免重新读取未更改的目录,updatedb通常由cron每天运行以更新默认数据库. 语法 ...
- SpringBoot+Shiro+LayUI权限管理系统项目-4.实现部门管理
1.说明 只讲解关键部分,详细看源码,文章下方捐赠或QQ联系捐赠获取. 2.功能展示 3.业务模型 @Data @EqualsAndHashCode(callSuper = false) @Acces ...
- Vue+SpringBoot+ElementUI实战学生管理系统-5.用户管理模块
1.章节介绍 前一篇介绍了项目的API接口设计,这一篇编写用户管理模块,需要的朋友可以拿去自己定制.:) 2.获取源码 源码是捐赠方式获取,详细请QQ联系我 :)! 3.项目截图 列表操作 动态图 4 ...
- MYSQL服务无法启动:InnoDB: .\ibdata1 can't be opened in read-write mode
今天在那做实验倒腾mysql数据库,后来发现服务无法启动,查看日志报错如下: 2015-01-07 17:48:54 9136 [ERROR] InnoDB: .\ibdata1 can't be o ...
- 我的小程序之旅十:微信公众号token验证失败
为了更好的运营公众号,微信官方支持用户自定义实现公众号功能,这里第一步就是配置服务器回调域名,如下图: 如果是SpringBoot项目,我们会写一个如下的Controller类 import lomb ...
- std::shared_ptr 线程安全方面的思考
一直惦记着 std::shared_ptr 线程安全的问题,看了些文章后,又怕过段时间忘记了,遂记录下来 std::shared_ptr 的线程安全问题主要有以下两种: 引用计数的加减操作是否线程安全 ...
- C++ GDAL用CreateCopy()新建栅格并修改波段的个数
本文介绍基于C++语言GDAL库,为CreateCopy()函数创建的栅格图像添加更多波段的方法. 在C++语言的GDAL库中,我们可以基于CreateCopy()函数与Create()函数创 ...
- 【Azure Logic App】添加 Storage Account 来提升 Logic App 的性能
文章原文:https://techcommunity.microsoft.com/t5/azure-integration-services-blog/scaling-logic-app-standa ...
- 【Azure 环境】Azure 的PaaS服务如果涉及到安全漏洞问题后,我们如何确认所用服务的实例(VM:虚拟机)的操作系统已修复该补丁呢?
问题描述 如上图中PaaS所不可见区域, 操作系统级别的内容我们并不知道具体的内容.如果当出现新的操作系统级别的安全漏洞时候,我们如何来确认当前所使用的Azure PaaS服务所在主机的OS已经修复了 ...