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渲染模块的Buffer
以下大致是一个最简的WebGL绘制代码:
<canvas id="canvas"></canvas>
<script>
const vertexSource = `
attribute vec3 aPos;
void main()
{
gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
}
`
const fragmentSource = `
void main()
{
gl_FragColor = vec4(1.0, 0.5, 0.2, 1.0);
}
`
const canvas = document.getElementById('canvas');
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
const gl = canvas.getContext('webgl2');
if (!gl) {
alert('WebGL not supported');
}
const vertices = new Float32Array([
-0.5, -0.5, 0.0,
0.5, -0.5, 0.0,
0.0, 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 shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
gl.clearColor(0.2, 0.3, 0.3, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(shaderProgram);
gl.drawArrays(gl.TRIANGLES, 0, 3);
</script>
其中,vbo
是顶点缓冲对象(Vertex Buffer Objects, VBO),主要用来储存顶点数据:
const vertices = new Float32Array([
-0.5, -0.5, 0.0,
0.5, -0.5, 0.0,
0.0, 0.5, 0.0,
]);
const vbo = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
WebGLBuffer 接口属于 WebGL API 的一部分,表示一个不透明的缓冲区对象,储存诸如顶点或着色之类的数据
WebGLBuffer
对象没有定义任何自己的方法或属性,且内容不能被直接访问。当使用 WebGLBuffer
对象时, WebGLRenderingContext
下的这些方法会很有用:
WebGLRenderingContext.bindBuffer()
WebGLRenderingContext.createBuffer()
WebGLRenderingContext.deleteBuffer()
WebGLRenderingContext.isBuffer()
示例代码中:
createBuffer()
方法可创建并初始化一个用于储存顶点数据或着色数据的WebGLBuffer
对象
bindBuffer(target, buffer)
方法将给定的WebGLBuffer
绑定到目标,参数:
target
GLenum
(en-US) 指定绑定点 (target)。可能的值:
gl.ARRAY_BUFFER
: 包含顶点属性的 Buffer,如顶点坐标,纹理坐标数据或顶点颜色数据。gl.ELEMENT_ARRAY_BUFFER
: 用于元素索引的 Bufferbuffer
要绑定的 WebGLBuffer
bufferData(target, size, usage)
方法创建并初始化了 Buffer 对象的数据存储区,参数:
target
GLenum
(en-US) 指定 Buffer 绑定点(目标)。可取以下值:
gl.ARRAY_BUFFER
: 包含顶点属性的 Buffer,如顶点坐标,纹理坐标数据或顶点颜色数据。gl.ELEMENT_ARRAY_BUFFER
: 用于元素索引的 Buffersize
GLsizeiptr
(en-US) 设定 Buffer 对象的数据存储区大小usage
GLenum
(en-US) 指定数据存储区的使用方法。可取以下值:
gl.STATIC_DRAW
: 缓冲区的内容可能经常使用,而不会经常更改。内容被写入缓冲区,但不被读取。gl.DYNAMIC_DRAW
: 缓冲区的内容可能经常被使用,并且经常更改。内容被写入缓冲区,但不被读取。gl.STREAM_DRAW
: 缓冲区的内容可能不会经常使用。内容被写入缓冲区,但不被读取以上大致就是WebGL中Buffer(VBO)的用法
Cesium中,对WebGL的Buffer进行了封装:
function Buffer(options) {
const gl = options.context._gl;
gl.bindBuffer(bufferTarget, buffer);
gl.bufferData(bufferTarget, hasArray ? typedArray : sizeInBytes, usage);
gl.bindBuffer(bufferTarget, null);
}
Buffer.createVertexBuffer = function (options) {
return new Buffer({
context: options.context,
bufferTarget: WebGLConstants.ARRAY_BUFFER,
typedArray: options.typedArray,
sizeInBytes: options.sizeInBytes,
usage: options.usage,
});
};
Buffer.createIndexBuffer = function (options) {
const buffer = new Buffer({
// ...
});
return buffer;
};
Buffer类的构造函数只是一次简单的封装,Buffer.createVertexBuffer
函数可以更为快速的构建Buffer,搜索源码就会发现大多也是使用Buffer.createVertexBuffer
创建Buffer,比如GeoJsonLoader.js
:
// ...
// Create GPU buffers
const positionBuffer = Buffer.createVertexBuffer({
typedArray: positionsTypedArray,
context: frameState.context,
usage: BufferUsage.STATIC_DRAW,
});
positionBuffer.vertexArrayDestroyable = false;
const featureIdBuffer = Buffer.createVertexBuffer({
typedArray: featureIdsTypedArray,
context: frameState.context,
usage: BufferUsage.STATIC_DRAW,
});
featureIdBuffer.vertexArrayDestroyable = false;
const indexBuffer = Buffer.createIndexBuffer({
typedArray: indicesTypedArray,
context: frameState.context,
usage: BufferUsage.STATIC_DRAW,
indexDatatype: indexDatatype,
});
indexBuffer.vertexArrayDestroyable = false;
// ...
使用Buffer.createVertexBuffer
创建指定数据大小而数据未确定的VBO的例子:
// Example 1. Create a dynamic vertex buffer 16 bytes in size.
const buffer = Buffer.createVertexBuffer({
context : context,
sizeInBytes : 16,
usage : BufferUsage.DYNAMIC_DRAW
});
使用Buffer.createVertexBuffer
创建数据确定的VBO的例子:
// Example 2. Create a dynamic vertex buffer from three floating-point values.
// The data copied to the vertex buffer is considered raw bytes until it is
// interpreted as vertices using a vertex array.
const positionBuffer = buffer.createVertexBuffer({
context : context,
typedArray : new Float32Array([0, 0, 0]),
usage : BufferUsage.STATIC_DRAW
});
使用Buffer.createIndexBuffer
创建指定数据大小而数据未确定的IBO的例子:
// Example 1. Create a stream index buffer of unsigned shorts that is
// 16 bytes in size.
const buffer = Buffer.createIndexBuffer({
context : context,
sizeInBytes : 16,
usage : BufferUsage.STREAM_DRAW,
indexDatatype : IndexDatatype.UNSIGNED_SHORT
});
使用Buffer.createIndexBuffer
创建数据确定的IBO的例子:
// Example 2. Create a static index buffer containing three unsigned shorts.
const buffer = Buffer.createIndexBuffer({
context : context,
typedArray : new Uint16Array([0, 1, 2]),
usage : BufferUsage.STATIC_DRAW,
indexDatatype : IndexDatatype.UNSIGNED_SHORT
});
此外,Buffer封装的函数有:
Buffer.prototype.copyFromArrayView = function (arrayView, offsetInBytes)
Buffer.prototype.copyFromBuffer = function (
readBuffer,
readOffset,
writeOffset,
sizeInBytes
)
Buffer.prototype.getBufferData = function (
arrayView,
sourceOffset,
destinationOffset,
length
)
Buffer.prototype.isDestroyed
Buffer.prototype.destroy
[1]Cesium原理篇:6 Renderer模块(1: Buffer) - fu*k - 博客园 (cnblogs.com)