WebGL1 到WebGL2

1,448 阅读3分钟

juejin.cn/post/684490… webgl2fundamentals.org/webgl/lesso…

发展史

  • image.png
  • 2015 OpenglES 3.2
  • 2018 Webgl2.0 的扩展 (基于OpenGLES 3.1)

功能增强

  • 渲染管道增强:遮挡查询,变换反馈,实例渲染
  • 着色语言:整数和32位浮点运算支持
  • 纹理功能增强
    • 浮点纹理,3D纹理, 深度纹理,顶点纹理, NPOT纹理, R/RG纹理,不可变纹理, 2D阵列纹理
    • ASTC压缩:减少处理纹理的内存占用和带宽
      • 使用JPG或PNG图片格式,使用texImage2D上传图片数据到GPU时会进行解压,转为位图。位图格式占用空间较大,传输时间较长。
      • 使用压缩格式,则可以不进行解压,同时节省空间和时间
      • WebGL1中通过扩展可以使用压缩纹理格式,但很多是要区分硬件条件的,S3TC基本上只是PC支持,PVTC只有iOS,而WebGL2中多种压缩格式都可以在任何环境下得到支持。
      • WebGL2还支持了纹理数组,多个相同大小的纹理切片可以共享一个纹理单元。纹理的访问也可以支持直接选取纹素。
    • 浮点渲染目标:提高高精度计操作的灵活性
  • Compute Shaders,独立的顶点和片元着色器,间接绘图命令
  • 几何和曲面细分着色器:有效处理GPU的复杂场景
  • 无需扩展就能使用的重要特性
    • image.png
  • 多绘制缓冲
    • 多绘制缓冲,也叫做MRT(Multiple Render Targets),是促使我们迁移到WebGL2的动力之一。它可以支持在一个帧缓冲区上绑定多个颜色缓冲区,一次绘制可以在多个缓冲区中写入数据,可用于实现延迟渲染和泛光效果。运用这项技术后,光照及阴影的计算只需要在每个片元上执行一次,而且可以尽量和几何渲染过程解耦。
  • Uniform缓冲对象
    • Uniform Buffer Object可以让我们像attribute变量一样,把uniform变量写入缓冲区
    • 使用gl.bufferData和gl.bindBufferRange两次调用替代N次gl.uniformXXX
    • 当多个着色器共用一些uniform变量时,可以一次写入,多处绑定即可

WebGL1迁移WebGL2

  • 上下文兼容处理:优先获取webgl2上下文
    •   // 优先使用WebGL2,若不支持则降级为WebGL(Safari/QQ浏览器)
        let gl = canvas.getContext('webgl2', options);
        if (gl) {
          gl.isWebGL2 = true;
        } else {
          gl = canvas.getContext('webgl', options);
          gl.isWebGL2 = false;
        }
      
  • 扩展兼容处理:将扩展方法复制到上下文对象
    •    function getAndApplyExtension(gl, name) {
          const ext = gl.getExtension(name);
          if (!ext) {
            return null;
          }
          const fnSuffix = name.split("_")[0];
          const enumSuffix = '_' + fnSuffix;
          for (const key in ext) {
            const value = ext[key];
            const isFunc = typeof (value) === 'function';
            const suffix = isFunc ? fnSuffix : enumSuffix;
            let name = key;
            // WEBGL_compressed_texture_s3tc
            // 和WEBGL_compressed_texture_pvrtc不是true
            if (key.endsWith(suffix)) {
              name = key.substring(0, key.length - suffix.length);
            }
            if (gl[name] !== undefined) {
              if (!isFunc && gl[name] !== value) {
                console.warn("conflict:", name, gl[name], value, key);
              }
            } else {
              if (isFunc) {
                gl[name] = function(origFn) {
                  return function() {
                    return origFn.apply(ext, arguments);
                  };
                }(value);
              } else {
                gl[name] = value;
              }
            }
          }
          return ext;
        }
      
        if (!gl.isWebGL2) {
          getAndApplyExtension(gl, 'ANGLE_instanced_arrays');
          getAndApplyExtension(gl, 'WEBGL_depth_texture');
          getAndApplyExtension(gl, 'OES_vertex_array_object');
        }
      
  • texImage2D的语法
    •   // WebGL1:
        void gl.texImage2D(target, level, internalformat, width, height, border, format, type, ArrayBufferView? pixels);
        void gl.texImage2D(target, level, internalformat, format, type, ImageData? pixels);
        void gl.texImage2D(target, level, internalformat, format, type, HTMLImageElement? pixels);
        void gl.texImage2D(target, level, internalformat, format, type, HTMLCanvasElement? pixels);
        void gl.texImage2D(target, level, internalformat, format, type, HTMLVideoElement? pixels);
        void gl.texImage2D(target, level, internalformat, format, type, ImageBitmap? pixels);
      
        // WebGL2:
        void gl.texImage2D(target, level, internalformat, width, height, border, format, type, GLintptr offset);
        void gl.texImage2D(target, level, internalformat, width, height, border, format, type, HTMLCanvasElement source);
        void gl.texImage2D(target, level, internalformat, width, height, border, format, type, HTMLImageElement source); 
        void gl.texImage2D(target, level, internalformat, width, height, border, format, type, HTMLVideoElement source); 
        void gl.texImage2D(target, level, internalformat, width, height, border, format, type, ImageBitmap source);
        void gl.texImage2D(target, level, internalformat, width, height, border, format, type, ImageData source);
        void gl.texImage2D(target, level, internalformat, width, height, border, format, type, ArrayBufferView srcData, srcOffset);
      
  • 深度纹理格式
    • 在WebGL2中internalformat不可使用gl.DEPTH_COMPONENT(虽然有这个属性)
    • 在WebLG1中不可使用gl.DEPTH_COMPONENT16(虽然有这个属性)
    • 上传时就能统一使用gl.DEPTH_COMPONENT24
    • WebGL1 internalformat和format 都是相同的
    • WebGL2 对应的format都是gl.DEPTH_COMPONENT
    • WebGL2 对应的internalformat 是gl.DEPTH_COMPONENT16还是gl.DEPTH_COMPONENT24、gl.DEPTH_COMPONENT32F
    •   if (!gl.isWebGL2) {
          gl.DEPTH_COMPONENT24 = gl.DEPTH_COMPONENT;
        }
      
  • 深度纹理过滤
    • 这里需要设置为gl.NEAREST才能正常使用。
    •   gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, options.minFilter || gl.LINEAR);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, options.magFilter || gl.LINEAR);
      
  • 着色器无需升级
    • 需要GLSL3.0 就加上#version 300 es即可