首页 > 其他分享 >Cesium渲染模块之Shader - 当时明月在曾照彩云归

Cesium渲染模块之Shader - 当时明月在曾照彩云归

时间:2023-03-09 21:11:33浏览次数:56  
标签:const Shader shader shaderProgram Cesium gl 着色器 曾照

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渲染模块的Shader

2. WebGL中的Shader

以下大致是一个最简的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>

image-20230227214406870

其中,着色器(Shader)是运行在GPU上的小程序,这些小程序为图形渲染管线的某个特定部分而运行,从基本意义上来说,着色器只是一种把输入转化为输出的程序。着色器可以是一个顶点着色器(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);

创建着色器的步骤大致为:

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

创建着色器程序的步骤大致为:

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

// Use the program
gl.useProgram(shaderProgram);
// Draw a single triangle
gl.drawArrays(gl.TRIANGLES, 0, 3);

3. Cesium中的Shader

Cesium渲染模块中的Shader对象包含从创建GLSL到创建Shader Program整个流程

流程大致为:

image-20230309155628504

  • Cesium中支持分段编写GLSL代码,包括ShaderStruct、ShaderFunction、ShaderDestination
  • 将分段的代码组合成GLSL,即ShaderSource
  • ShaderBuilder使用ShaderSource创建的ShaderProgram会缓存起来,即ShaderCache
  • 需要新的ShaderProgram时,先查询缓存中是否有,有就复用,无则创建

在Cesium源码中创建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)

[2]WebGLShader - Web API 接口参考 | MDN (mozilla.org)

[3]Cesium原理篇:6 Render模块(3: Shader) - fu*k - 博客园 (cnblogs.com)

[4]Cesium渲染模块之概述 - 当时明月在曾照彩云归 - 博客园 (cnblogs.com)

[5]Cesium DrawCommand 1 不谈地球 画个三角形 - 岭南灯火 - 博客园 (cnblogs.com)

本文转自 https://www.cnblogs.com/jiujiubashiyi/p/17199285.html,如有侵权,请联系删除。

标签:const,Shader,shader,shaderProgram,Cesium,gl,着色器,曾照
From: https://www.cnblogs.com/hustshu/p/17201429.html

相关文章

  • shadertoy学习 第3课
    注意下面的segment函数,计算了uv点到直线的距离,小于width就是白色//封装绘制网格函数vec3grid(vec2uv){vec2cell=fract(uv);//取小数部分vec3col=vec3(0.);//网格绘制......
  • GLSL shader学习系列2-画个圆
    这是GLSLshader学习系列的第二篇文章,本文中我们将会学习如何使用shader绘制一个圆形。引子上一篇文章的内容比较简单易懂,由于gl_FragCoord能够表示每个像素点在画布中的......
  • 三大 Shader 编程语言(CG/HLSL/GLSL)
    什么是ShaderLanguageShaderLanguage的发展方向是设计出在便携性方面可以和C++、Java等相比的高级语言,“赋予程序员灵活而方便的编程方式”,并“尽可能的控制渲染过程”......
  • Cesium性能优化
    ImprovingPerformancewithExplicitRendering:https://cesium.com/blog/2018/01/24/cesium-scene-rendering-performance/默认情况下,Cesium会像游戏引擎一样渲染新帧:以......
  • shadertoy学习 第2课
    坐标系统绘制与fwidth函数voidmainImage(outvec4fragColor,invec2fragCoord){//Normalizedpixelcoordinates(from-1to1)vec2uv=(2.0*fragCoord-iResolut......
  • # shader——无限循环背景的制作
    制作前准备需求要求UI中的一张图片需要无限制的斜向移动。图片设置准备开始制作首先我们需要一个mainTexture的参数和调节速度的参数,所以我们在Properties块中......
  • shadertoy学习 第一课
    一、坐标归一化voidmainImage(outvec4fragColor,invec2fragCoord){   //Normalizedpixelcoordinates(from0to1)    vec2uv=fragCoord/iResolut......
  • Cesium 几何体贴模型 sampleHeight(二十二)
    sampleHeight(position, objectsToExclude, width)返回给定地图位置处场景几何体的高度,如果没有要从中采样高度的场景几何体,则返回未定义的高度。输入位置的高度被忽略......
  • Cesium OSM building && Cesium widget(二十一)
     加载OSM以下是官方示例,但增加了丰富的注释//创建一个Cesium.Viewer对象,用于在网页中显示3D地球仪和地图//cesiumContainer是HTML元素的ID,用于容纳地球仪//terra......
  • Cesium 超出部分裁剪(二十一)
    以下为官网示例,但是我加上了详细的注释  //创建一个Cesium.Viewer对象,用于在网页中显示3D地球仪和地图//cesiumContainer是HTML元素的ID,用于容纳地球仪//terrai......