Cesium渲染模块之Shader

科技资讯 投稿 19100 0 评论

Cesium渲染模块之Shader

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.createShader(
  • 初始化着色器

  • 通过 WebGLRenderingContext.shaderSource( 挂接 GLSL 源代码

  • 最后调用 WebGLRenderingContext.compileShader( 完成着色器(shader)的编译

WebGLShader 仍不是可用的形式,它需要被添加到一个 WebGLProgram

  • 使用

  • WebGLRenderingContext.createProgram(
  • 初始化着色器程序

  • 通过 WebGLRenderingContext.attachShader( 然后附着顶点着色器和片段着色器

  • 最后调用 WebGLRenderingContext.linkProgram( 完成着色器(shader)的连接

使用着色器程序(上述代码中):

// 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

编程笔记 » Cesium渲染模块之Shader

赞同 (38) or 分享 (0)
游客 发表我的评论   换个身份
取消评论

表情
(0)个小伙伴在吐槽