1. 引言
Cesium官网:Cesium: The Platform for 3D Geospatial
API文档:Index - Cesium Documentation
Cesium教程系列汇总 - fu*k - 博客园 (cnblogs.com
开源GIS/Cesium源码 - 随笔分类 - 四季留歌 - 博客园 (cnblogs.com
渲染是前端可视化的核心,本文描述Cesium渲染模块的Shader
2. WebGL中的Shader
<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>
输入转化为输出的程序。着色器可以是一个顶点着色器(vertex shader)或片元着色器(fragment shader),每个ShaderProgram都需要这两种类型的着色器。上述代码中创建着色器和着色器程序的代码:
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;
创建着色器的步骤大致为:
使用
初始化着色器
通过
WebGLRenderingContext.shaderSource(
挂接 GLSL 源代码最后调用
WebGLRenderingContext.compileShader(
完成着色器(shader)的编译
WebGLRenderingContext.createShader(
WebGLShader 仍不是可用的形式,它需要被添加到一个 WebGLProgram
里
使用
初始化着色器程序
通过
WebGLRenderingContext.attachShader(
然后附着顶点着色器和片段着色器最后调用
WebGLRenderingContext.linkProgram(
完成着色器(shader)的连接
WebGLRenderingContext.createProgram(
使用着色器程序(上述代码中):
// Use the program
gl.useProgram(shaderProgram;
// Draw a single triangle
gl.drawArrays(gl.TRIANGLES, 0, 3;
3. Cesium中的Shader
Cesium渲染模块中的Shader对象包含从创建GLSL到创建Shader Program整个流程
Cesium中支持分段编写GLSL代码,包括ShaderStruct、ShaderFunction、ShaderDestination
将分段的代码组合成GLSL,即ShaderSource
ShaderBuilder使用ShaderSource创建的ShaderProgram会缓存起来,即ShaderCache
需要新的ShaderProgram时,先查询缓存中是否有,有就复用,无则创建
PolylineCollection.js:
const fs = new ShaderSource({
defines: defines,
sources: ["in vec4 v_pickColor;\n", this.material.shaderSource, PolylineFS],
};
const vsSource = batchTable.getVertexShaderCallback((PolylineVS;
const vs = new ShaderSource({
defines: defines,
sources: [PolylineCommon, vsSource],
};
this.shaderProgram = ShaderProgram.fromCache({
context: context,
vertexShaderSource: vs,
fragmentShaderSource: fs,
attributeLocations: attributeLocations,
};
ShaderProgram.fromCache
只是简单的指向ShaderCache.getShaderProgram
ShaderProgram.fromCache = function (options {
// ...
return options.context.shaderCache.getShaderProgram(options;
};
ShaderCache.getShaderProgram
逻辑就是先查询缓存中是否有Shader,有就复用,无则创建:
ShaderCache.prototype.getShaderProgram = function (options {
// ...
let cachedShader;
if (defined(this._shaders[keyword] {
cachedShader = this._shaders[keyword];
} else {
const shaderProgram = new ShaderProgram(;
cachedShader = {
cache: this,
shaderProgram: shaderProgram,
keyword: keyword,
derivedKeywords: [],
count: 0,
};
}
return cachedShader.shaderProgram;
};
注意,此时的ShaderProgram只是个空壳,它并没有真正的创建WebGLProgram对象,但是它具备了创建WebGLProgram对象所需要的条件
PointCloud.js:
function createShaders({
// ...
drawCommand.shaderProgram = ShaderProgram.fromCache({
context: context,
vertexShaderSource: vs,
fragmentShaderSource: fs,
attributeLocations: attributeLocations,
};
drawCommand.shaderProgram._bind(;
}
所以shaderProgram._bind(
才创建了WebGLProgram对象
ShaderProgram.prototype._bind = function ( {
initialize(this;
this._gl.useProgram(this._program;
};
function initialize(shader {
// ...
reinitialize(shader;
}
function reinitialize(shader {
// ...
const program = createAndLinkProgram(gl, shader, shader._debugShaders;
}
通过多处调用,最后createAndLinkProgram(gl, shader
实现了创建:
function createAndLinkProgram(gl, shader {
const vsSource = shader._vertexShaderText;
const fsSource = shader._fragmentShaderText;
const vertexShader = gl.createShader(gl.VERTEX_SHADER;
gl.shaderSource(vertexShader, vsSource;
gl.compileShader(vertexShader;
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER;
gl.shaderSource(fragmentShader, fsSource;
gl.compileShader(fragmentShader;
const program = gl.createProgram(;
gl.attachShader(program, vertexShader;
gl.attachShader(program, fragmentShader;
const attributeLocations = shader._attributeLocations;
if (defined(attributeLocations {
for (const attribute in attributeLocations {
if (attributeLocations.hasOwnProperty(attribute {
gl.bindAttribLocation(
program,
attributeLocations[attribute],
attribute
;
}
}
}
gl.linkProgram(program;
gl.deleteShader(vertexShader;
gl.deleteShader(fragmentShader;
// ...
return program;
}
值得一说的是,真正的WebGLProgram对象是直到需要绘制时才创建,不需要绘制的就不会创建,这样有效节省了资源:
function beginDraw(context, framebuffer, passState, shaderProgram, renderState {
// ...
bindFramebuffer(context, framebuffer;
applyRenderState(context, renderState, passState, false;
shaderProgram._bind(;
}
4. 参考资料
[1]WebGLProgram - Web API 接口参考 | MDN (mozilla.org
[3]Cesium原理篇:6 Render模块(3: Shader - fu*k - 博客园 (cnblogs.com
[5]Cesium DrawCommand 1 不谈地球 画个三角形 - 岭南灯火 - 博客园 (cnblogs.com