渲染概述
作者:当时明月在曾照彩云归
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的渲染模块概述
2. WebGL渲染流程
WebGL(或者说OpenGL)的绘制流程(图形渲染管线,Graphics Pipeline)如下:
- 顶点着色(vertex shader)阶段将CPU传入的数据进行一定的变换处理
- 图元装配(shape assembly)阶段的就是上阶段的顶点数据处理成图元(如,三角形)
- 几何着色(geometry shader)阶段是根据一定规则将输入的图元变更或输出更多的图元(可选)
- 光栅化(rasterization)阶段的是将上阶段的图元进行计算得到图元占据的屏幕像素列表
- 片元着色(fragment)阶段是将上阶段生成的片元进行着色处理后
- 测试与混合阶段计算片元的深度、颜色等从而进行舍弃或保留
绘制流程繁琐,然而,我们能配置的只有三个蓝色的着色器部分。几何着色器可选,一般配置顶点着色器和片段着色器即可,即,以下步骤就是配置顶点着色器和片段着色器
2.1 生成顶点数据
生成顶点缓冲对象(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 vertexBuffer = gl.createBuffer(); |
|
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); |
|
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); |
2.2 链接属性数据
顶点数组对象(Vertex Array Object, VAO)与VBO绑定,用于保存属性数据(先绑定VAO,再创建VBO就会绑定到VAO上):
const vao = gl.createVertexArray(); |
|
gl.bindVertexArray(vao); |
|
gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0); |
|
gl.enableVertexAttribArray(0) |
2.3 创建顶点着色器
创建顶点着色器(Vertex Shader)并编译:
const vertexShader = gl.createShader(gl.VERTEX_SHADER); |
|
gl.shaderSource(vertexShader, ` |
|
attribute vec3 aPos; |
|
void main() |
|
{ |
|
gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0); |
|
} |
|
`); |
|
gl.compileShader(vertexShader); |
2.4 创建片段着色器
创建片段着色器(Fragment Shader)并编译:
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); |
|
gl.shaderSource(fragmentShader, ` |
|
#version 100 |
|
void main() |
|
{ |
|
gl_FragColor = vec4(1.0, 0.5, 0.2, 1.0); |
|
} |
|
`); |
|
gl.compileShader(fragmentShader); |
2.5 链接着色器
着色器程序对象(Shader Program Object)是多个着色器合并之后并最终链接完成的版本:
const shaderProgram = gl.createProgram(); |
|
gl.attachShader(shaderProgram, vertexShader); |
|
gl.attachShader(shaderProgram, fragmentShader); |
|
gl.linkProgram(shaderProgram); |
2.6 绘制
开始绘制:
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); |
2.7 流程总结
上述几乎可以说是最简化的WebGL渲染流程,包含的基本要素有
- VBO,顶点缓冲对象 --------> 存顶点数据
- VAO,顶点数组对象(WebGL 2支持)--------> 存顶点属性
- Shader,着色器(包括顶点和片段着色器) --------> 运算和渲染
- Shader Program,着色器程序 --------> 连接着色器
此外,常用的还有:
- Texture,纹理(通常是一张图片)
- Framebuffer,帧缓冲对象(FBO)
- Renderbuffer,渲染缓冲对象(RBO)
- CubeMap,立方体贴图
3. Cesium渲染模块
查看Cesium渲染模块中的文件:
$ ls packages/engine/Source/Renderer/ |
|
AutomaticUniforms.js |
|
Buffer.js |
|
BufferUsage.js |
|
ClearCommand.js |
|
ComputeCommand.js |
|
ComputeEngine.js |
|
Context.js |
|
ContextLimits.js |
|
CubeMap.js |
|
CubeMapFace.js |
|
DrawCommand.js |
|
Framebuffer.js |
|
FramebufferManager.js |
|
MipmapHint.js |
|
MultisampleFramebuffer.js |
|
Pass.js |
|
PassState.js |
|
PixelDatatype.js |
|
RenderState.js |
|
Renderbuffer.js |
|
RenderbufferFormat.js |
|
Sampler.js |
|
ShaderBuilder.js |
|
ShaderCache.js |
|
ShaderDestination.js |
|
ShaderFunction.js |
|
ShaderProgram.js |
|
ShaderSource.js |
|
ShaderStruct.js |
|
Texture.js |
|
TextureCache.js |
|
TextureMagnificationFilter.js |
|
TextureMinificationFilter.js |
|
TextureWrap.js |
|
UniformState.js |
|
VertexArray.js |
|
VertexArrayFacade.js |
|
createUniform.js |
|
createUniformArray.js |
|
demodernizeShader.js |
|
freezeRenderState.js |
|
loadCubeMap.js |
通过文件名,不难发现,Cesium的渲染模块主要包含:
- Buffer
- VAO
- Shader
- Texture
- Framebuffer
- Renderbuffer
- CubeMap
以上是WebGL渲染流程常见的对象,另外Cesium渲染模块还有:
- Command,指令对象(包含ClearCommand、ComputeCommand、DrawCommand)
- Pass,通道(渲染顺序)
- Sampler,采样器
- Uniform,GPU全局变量
- Context,WebGL上下文
3.1 Context对象
相关代码文件:
- Context.js
- ContextLimits.js
Context对象是对WebGL上下文对象的封装,包含Pass、Uniform等,并且对WebGL 1.0和WebGL 2.0做了兼容处理:
function Context(canvas, options) { |
|
this._canvas = canvas; |
|
this._originalGLContext = glContext; |
|
this._gl = glContext; |
|
this._webgl2 = webgl2; |
|
this._id = createGuid(); |
|
this._clearColor = new Color(0.0, 0.0, 0.0, 0.0); |
|
this._clearDepth = 1.0; |
|
this._clearStencil = 0; |
|
// ... |
|
const us = new UniformState(); |
|
const ps = new PassState(this); |
|
const rs = RenderState.fromCache(); |
|
this._defaultPassState = ps; |
|
this._defaultRenderState = rs; |
|
// ... |
|
RenderState.apply(gl, rs, ps); |
|
} |
|
3.2 Buffer对象
相关代码文件:
- Buffer.js
- BufferUsage.js
Buffer对象主要包含顶点缓冲对象(Vertex Buffer Objects, VBO)和索引缓冲对象(Index Buffer Object,IBO)
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({ |
|
// ... |
|
}); |
|
}; |
|
Buffer.createIndexBuffer = function (options) { |
|
const buffer = new Buffer({ |
|
// ... |
|
}); |
|
return buffer; |
|
}; |
3.3 VAO对象
相关代码文件:
- VertexArray.js
- VertexArrayFacade.js
顶点数组对象(Vertex Array Object, VAO)与VBO绑定,用于保存属性数据
function VertexArray(options) { |
|
// ... |
|
const context = options.context; |
|
const gl = context._gl; |
|
const attributes = options.attributes; |
|
const indexBuffer = options.indexBuffer; |
|
let vao; |
|
vao = context.glCreateVertexArray(); |
|
context.glBindVertexArray(vao); |
|
bind(gl, vaAttributes, indexBuffer); |
|
context.glBindVertexArray(null); |
|
} |
3.4 Shader对象
相关代码文件:
- ShaderBuilder.js
- ShaderCache.js
- ShaderStruct.js
- ShaderSource.js
- ShaderProgram.js
- ShaderFunction.js
- ShaderDestination.js
Cesium渲染模块中的Shader对象包含从创建GLSL到创建Shader Program整个流程
流程大致为:
- Cesium中支持分段编写GLSL代码,包括ShaderStruct、ShaderFunction、ShaderDestination
- 将分段的代码组合成GLSL,即ShaderSource
- ShaderBuilder使用ShaderSource创建的ShaderProgram会缓存起来,即ShaderCache
- 需要新的ShaderProgram时,先查询缓存中是否有,有就复用,无则创建
3.5 Texture对象
主要代码文件:
- Texture.js
- TextureCache.js
- TextureMagnificationFilter.js
- TextureMinificationFilter.js
- TextureWrap.js
Cesium中的Texture对象是对WebGL中Texture对象的进一步封装:
function Texture(options) { |
|
// ... |
|
const context = options.context; |
|
const gl = context._gl; |
|
const textureTarget = gl.TEXTURE_2D; |
|
const texture = gl.createTexture(); |
|
gl.activeTexture(gl.TEXTURE0); |
|
gl.bindTexture(textureTarget, texture); |
|
// Source: ImageData, HTMLImageElement, HTMLCanvasElement, or HTMLVideoElement |
|
gl.texImage2D( |
|
textureTarget, |
|
0, |
|
internalFormat, |
|
pixelFormat, |
|
PixelDatatype.toWebGLConstant(pixelDatatype, context), |
|
source |
|
); |
|
gl.bindTexture(textureTarget, null); |
|
this.sampler = defined(options.sampler) ? options.sampler : new Sampler(); |
|
} |
3.6 Framebuffer对象
主要代码文件:
- Framebuffer.js
- FramebufferManager.js
- MultisampleFramebuffer.js
Cesium中的Framebuffer对象是对WebGL中Framebuffer对象的进一步封装:
function Framebuffer(options) { |
|
// ... |
|
this._gl = gl; |
|
this._framebuffer = gl.createFramebuffer(); |
|
this._bind(); |
|
this._colorTextures[i] = texture; |
|
this._colorRenderbuffers[i] = renderbuffer; |
|
this._depthTexture = texture; |
|
this._depthRenderbuffer = renderbuffer; |
|
this._stencilRenderbuffer = renderbuffer; |
|
this._depthStencilTexture = texture; |
|
this._depthStencilRenderbuffer = renderbuffer; |
|
this._unBind(); |
|
} |
3.7 Renderbuffer对象
主要代码文件:
- Renderbuffer.js
- RenderbufferFormat.js
Cesium中的Renderbuffer对象是对WebGL中Renderbuffer对象的进一步封装:
function Renderbuffer(options) { |
|
// ... |
|
const gl = context._gl; |
|
this._renderbuffer = this._gl.createRenderbuffer(); |
|
gl.bindRenderbuffer(gl.RENDERBUFFER, this._renderbuffer); |
|
gl.renderbufferStorage(gl.RENDERBUFFER, format, width, height); |
|
gl.bindRenderbuffer(gl.RENDERBUFFER, null); |
|
} |
3.8 CubeMap对象
主要代码文件:
- CubeMap.js
- CubeMapFace.js
- loadCubeMap.js
Cesium中的CubeMap对象是对WebGL中CubeMap对象的进一步封装:
function CubeMap(options) { |
|
// ... |
|
const gl = context._gl; |
|
const textureTarget = gl.TEXTURE_CUBE_MAP; |
|
const texture = gl.createTexture(); |
|
gl.activeTexture(gl.TEXTURE0); |
|
gl.bindTexture(textureTarget, texture); |
|
gl.texImage2D() |
|
// ... |
|
gl.bindTexture(textureTarget, null); |
|
this.sampler = defined(options.sampler) ? options.sampler : new Sampler(); |
|
} |
3.9 Command
主要代码文件:
- ClearCommand.js
- DrawCommand.js
- ComputeEngine.js
- ComputeCommand.js
Command对象主要有三类:
- ClearCommand
- DrawCommand
- ComputeCommand
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)
3.10 Pass
主要代码文件:
- Pass.js
- PassState.js
Pass是一种渲染优先级,数值越低,等级越高:
const Pass = { |
|
ENVIRONMENT: 0, |
|
COMPUTE: 1, |
|
GLOBE: 2, |
|
TERRAIN_CLASSIFICATION: 3, |
|
CESIUM_3D_TILE: 4, |
|
CESIUM_3D_TILE_CLASSIFICATION: 5, |
|
CESIUM_3D_TILE_CLASSIFICATION_IGNORE_SHOW: 6, |
|
OPAQUE: 7, |
|
TRANSLUCENT: 8, |
|
VOXELS: 9, |
|
OVERLAY: 10, |
|
NUMBER_OF_PASSES: 11, |
|
}; |
3.11 Sampler
主要代码文件:
- Sampler.js
Sampler是将采样方式封装:
function Sampler(options) { |
|
// ... |
|
this._wrapS = wrapS; |
|
this._wrapT = wrapT; |
|
this._minificationFilter = minificationFilter; |
|
this._magnificationFilter = magnificationFilter; |
|
this._maximumAnisotropy = maximumAnisotropy; |
|
} |
3.12 Uniform
主要代码文件:
- createUniform.js
- UniformState.js
- createUniformArray.js
Uniform是对WebGL中一系列Uniform的封装,比如UniformFloat:
function UniformFloat(gl, activeUniform, uniformName, location) { |
|
// ... |
|
this.name = uniformName; |
|
this.value = undefined; |
|
this._value = 0.0; |
|
this._gl = gl; |
|
this._location = location; |
|
} |
|
UniformFloat.prototype.set = function () { |
|
if (this.value !== this._value) { |
|
this._value = this.value; |
|
this._gl.uniform1f(this._location, this.value); |
|
} |
|
}; |
4. 参考资料
[1]WebGL2RenderingContext - Web APIs | MDN (mozilla.org)
[2]LearnOpenGL CN (learnopengl-cn.github.io)
[3]Cesium教程系列汇总 - fu*k - 博客园 (cnblogs.com)
[4]Renderbuffer Object - OpenGL Wiki (khronos.org)
[5]基于JavaScript的OpenGL 01 之Hello Triangle - 当时明月在曾照彩云归 - 博客园 (cnblogs.com)
GeoJSON加载
var promise = Cesium.GeoJsonDataSource.load('../Data/res024.json',{clampToGround: true});
promise.then(datasource=>{
viewer.dataSources.add(datasource);
datasource.entities.values.forEach(enetity=>{
console.log(enetity);
enetity.polygon.outlineColor=Cesium.Color.RED;
enetity.polygon.material=Cesium.Color.BLUE;
// enetity.polygon.height=1000;
// enetity.polygon.extrudedHeight=2000;
})
viewer.zoomTo(datasource)
})
位置调整