Cesium渲染模块之概述

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)如下:

img

  • 顶点着色(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整个流程

流程大致为:

image-20230309155628504

  • 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)

posted @ 2023-02-15 21:12  当时明月在曾照彩云归  阅读(1055)  评论(1编辑  收藏  举报