• 原文地址:Day 11. Reducing WebGL boilerplate
  • 原文作者:Andrei Lesnitsky

这是 WebGL 系列的第 11 天教程,每天都有新文章公布。

订阅或者退出邮件列表以便及时获取更新内容。

源代码在这里

第 10 天咱们曾经学习了如何应用多个纹理。这须要批改着色器以及 JavaScript 代码,而且可能会有局部主动实现更改。

有一个软件包 glsl-extract-sync 能够获取无关着色器 attributesuniforms

装置此软件包:

npm i glsl-extract-sync

???? package.json

      "url-loader": "^2.0.1",      "webpack": "^4.35.2",      "webpack-cli": "^3.3.5"+   },+   "dependencies": {+     "glsl-extract-sync": "0.0.0"    }  }

当初,让咱们创立一个辅助函数,该函数将在软件包的帮忙下获取对 attributesuniforms 的所有援用。

???? src/gl-helpers.js

+ import extract from 'glsl-extract-sync';+   export function compileShader(gl, shader, source) {      gl.shaderSource(shader, source);      gl.compileShader(shader);          img,      );  }+ + export function setupShaderInput(gl, program, vShaderSource, fShaderSource) {+ + }

咱们须要提取无关顶点着色器和片段着色器的信息

???? src/gl-helpers.js

  }    export function setupShaderInput(gl, program, vShaderSource, fShaderSource) {- +     const vShaderInfo = extract(vShaderSource);+     const fShaderInfo = extract(fShaderSource);  }

???? src/texture.js

  import vShaderSource from './shaders/texture.v.glsl';  import fShaderSource from './shaders/texture.f.glsl';- import { compileShader, loadImage, createTexture, setImage } from './gl-helpers';+ import { compileShader, loadImage, createTexture, setImage, setupShaderInput } from './gl-helpers';  import { createRect } from './shape-helpers';    import textureImageSrc from '../assets/images/texture.jpg';  gl.bindBuffer(gl.ARRAY_BUFFER, vertexPositionBuffer);  gl.bufferData(gl.ARRAY_BUFFER, vertexPosition, gl.STATIC_DRAW);  + console.log(setupShaderInput(gl, program, vShaderSource, fShaderSource));+   const attributeLocations = {      position: gl.getAttribLocation(program, 'position'),      texCoord: gl.getAttribLocation(program, 'texCoord'),

只有顶点着色器能够具备 attributes,然而能够在两个着色器中定义 uniforms

???? src/gl-helpers.js

  export function setupShaderInput(gl, program, vShaderSource, fShaderSource) {      const vShaderInfo = extract(vShaderSource);      const fShaderInfo = extract(fShaderSource);+ +     const attributes = vShaderInfo.attributes;+     const uniforms = [+         ...vShaderInfo.uniforms,+         ...fShaderInfo.uniforms,+     ];  }

当初咱们能够取得所有属性 attributes 地位

???? src/gl-helpers.js

          ...vShaderInfo.uniforms,          ...fShaderInfo.uniforms,      ];+ +     const attributeLocations = attributes.reduce((attrsMap, attr) => {+         attrsMap[attr.name] = gl.getAttribLocation(program, attr.name);+         return attrsMap;+     }, {});  }

并启用所有属性 attributes

???? src/gl-helpers.js

          attrsMap[attr.name] = gl.getAttribLocation(program, attr.name);          return attrsMap;      }, {});+ +     attributes.forEach((attr) => {+         gl.enableVertexAttribArray(attributeLocations[attr.name]);+     });  }

咱们还应该取得所有 uniform 的地位

???? src/gl-helpers.js

      attributes.forEach((attr) => {          gl.enableVertexAttribArray(attributeLocations[attr.name]);      });+ +     const uniformLocations = uniforms.reduce((uniformsMap, uniform) => {+         uniformsMap[uniform.name] = gl.getUniformLocation(program, uniform.name);+         return uniformsMap;+     }, {});  }

最初返回 attributeuniform 地位

???? src/gl-helpers.js

          uniformsMap[uniform.name] = gl.getUniformLocation(program, uniform.name);          return uniformsMap;      }, {});+ +     return {+         attributeLocations,+         uniformLocations,+     }  }

好吧,让咱们利用咱们的新小助手

???? src/texture.js

  gl.bindBuffer(gl.ARRAY_BUFFER, vertexPositionBuffer);  gl.bufferData(gl.ARRAY_BUFFER, vertexPosition, gl.STATIC_DRAW);  - console.log(setupShaderInput(gl, program, vShaderSource, fShaderSource));+ const programInfo = setupShaderInput(gl, program, vShaderSource, fShaderSource);  - const attributeLocations = {-     position: gl.getAttribLocation(program, 'position'),-     texCoord: gl.getAttribLocation(program, 'texCoord'),-     texIndex: gl.getAttribLocation(program, 'texIndex'),- };- - const uniformLocations = {-     texture: gl.getUniformLocation(program, 'texture'),-     otherTexture: gl.getUniformLocation(program, 'otherTexture'),-     resolution: gl.getUniformLocation(program, 'resolution'),- };- - gl.enableVertexAttribArray(attributeLocations.position);- gl.vertexAttribPointer(attributeLocations.position, 2, gl.FLOAT, false, 0, 0);+ gl.vertexAttribPointer(programInfo.attributeLocations.position, 2, gl.FLOAT, false, 0, 0);    gl.bindBuffer(gl.ARRAY_BUFFER, texCoordsBuffer);- - gl.enableVertexAttribArray(attributeLocations.texCoord);- gl.vertexAttribPointer(attributeLocations.texCoord, 2, gl.FLOAT, false, 0, 0);+ gl.vertexAttribPointer(programInfo.attributeLocations.texCoord, 2, gl.FLOAT, false, 0, 0);    gl.bindBuffer(gl.ARRAY_BUFFER, texIndiciesBuffer);- - gl.enableVertexAttribArray(attributeLocations.texIndex);- gl.vertexAttribPointer(attributeLocations.texIndex, 1, gl.FLOAT, false, 0, 0);+ gl.vertexAttribPointer(programInfo.attributeLocations.texIndex, 1, gl.FLOAT, false, 0, 0);    const vertexIndices = new Uint8Array([      // left rect        gl.activeTexture(gl.TEXTURE0);      gl.bindTexture(gl.TEXTURE_2D, texture);-     gl.uniform1i(uniformLocations.texture, 0);+     gl.uniform1i(programInfo.uniformLocations.texture, 0);        gl.activeTexture(gl.TEXTURE1);      gl.bindTexture(gl.TEXTURE_2D, otherTexture);-     gl.uniform1i(uniformLocations.otherTexture, 1);+     gl.uniform1i(programInfo.uniformLocations.otherTexture, 1);  -     gl.uniform2fv(uniformLocations.resolution, [canvas.width, canvas.height]);+     gl.uniform2fv(programInfo.uniformLocations.resolution, [canvas.width, canvas.height]);        gl.drawElements(gl.TRIANGLES, vertexIndices.length, gl.UNSIGNED_BYTE, 0);  });

看起来很像清理工????

咱们常常应用的另一个工具是缓冲区。
让咱们创立一个助手类

???? src/GLBuffer.js

export class GLBuffer {    constructor(gl, target, data) {    }}

咱们将须要数据、指标缓冲区和理论的 gl 缓冲区,因而让咱们调配从内部传递进来的所有内容并创立 gl 缓冲区。

???? src/GLBuffer.js

  export class GLBuffer {      constructor(gl, target, data) {- +         this.target = target;+         this.data = data;+         this.glBuffer = gl.createBuffer();      }  }

咱们没有将 gl 调配给 instance,因为它可能会导致内存透露,咱们须要从内部传递它

让咱们为 gl.bindBuffer 实现一个代替计划

???? src/GLBuffer.js

          this.data = data;          this.glBuffer = gl.createBuffer();      }+ +     bind(gl) {+         gl.bindBuffer(this.target, this.glBuffer);+     }  }

以及设置缓冲区数据的便捷办法

???? src/GLBuffer.js

      bind(gl) {          gl.bindBuffer(this.target, this.glBuffer);      }+ +     setData(gl, data, usage) {+         this.data = data;+         this.bind(gl);+         gl.bufferData(this.target, this.data, usage);+     }  }

当初让咱们 data 结构一个参数,并增加一个 usage 参数,使其可能通过结构函数调用实现咱们须要的所有操作

???? src/GLBuffer.js

  export class GLBuffer {-     constructor(gl, target, data) {+     constructor(gl, target, data, usage) {          this.target = target;          this.data = data;          this.glBuffer = gl.createBuffer();+ +         if (typeof data !== 'undefined') {+             this.setData(gl, data, usage);+         }      }        bind(gl) {

太酷了,当初咱们能够替换掉 texCoords 缓冲区

???? src/texture.js

    import textureImageSrc from '../assets/images/texture.jpg';  import textureGreenImageSrc from '../assets/images/texture-green.jpg';+ import { GLBuffer } from './GLBuffer';    const canvas = document.querySelector('canvas');  const gl = canvas.getContext('webgl');  gl.linkProgram(program);  gl.useProgram(program);  - const texCoords = new Float32Array([+ const texCoordsBuffer = new GLBuffer(gl, gl.ARRAY_BUFFER, new Float32Array([      ...createRect(0, 0, 1, 1), // left rect      ...createRect(0, 0, 1, 1), // right rect- ]);- const texCoordsBuffer = gl.createBuffer();- - gl.bindBuffer(gl.ARRAY_BUFFER, texCoordsBuffer);- gl.bufferData(gl.ARRAY_BUFFER, texCoords, gl.STATIC_DRAW);+ ]), gl.STATIC_DRAW);    const texIndicies = new Float32Array([      ...Array.from({ length: 4 }).fill(0), // left rect    gl.vertexAttribPointer(programInfo.attributeLocations.position, 2, gl.FLOAT, false, 0, 0);  - gl.bindBuffer(gl.ARRAY_BUFFER, texCoordsBuffer);+ texCoordsBuffer.bind(gl);  gl.vertexAttribPointer(programInfo.attributeLocations.texCoord, 2, gl.FLOAT, false, 0, 0);    gl.bindBuffer(gl.ARRAY_BUFFER, texIndiciesBuffer);

texIndices 缓冲区执行雷同的操作

???? src/texture.js

      ...createRect(0, 0, 1, 1), // right rect  ]), gl.STATIC_DRAW);  - const texIndicies = new Float32Array([+ const texIndiciesBuffer = new GLBuffer(gl, gl.ARRAY_BUFFER, new Float32Array([      ...Array.from({ length: 4 }).fill(0), // left rect      ...Array.from({ length: 4 }).fill(1), // right rect- ]);- const texIndiciesBuffer = gl.createBuffer();- - gl.bindBuffer(gl.ARRAY_BUFFER, texIndiciesBuffer);- gl.bufferData(gl.ARRAY_BUFFER, texIndicies, gl.STATIC_DRAW);+ ]), gl.STATIC_DRAW);    const vertexPosition = new Float32Array([      ...createRect(-1, -1, 1, 2), // left rect  texCoordsBuffer.bind(gl);  gl.vertexAttribPointer(programInfo.attributeLocations.texCoord, 2, gl.FLOAT, false, 0, 0);  - gl.bindBuffer(gl.ARRAY_BUFFER, texIndiciesBuffer);+ texIndiciesBuffer.bind(gl);  gl.vertexAttribPointer(programInfo.attributeLocations.texIndex, 1, gl.FLOAT, false, 0, 0);    const vertexIndices = new Uint8Array([

顶点坐标

???? src/texture.js

      ...Array.from({ length: 4 }).fill(1), // right rect  ]), gl.STATIC_DRAW);  - const vertexPosition = new Float32Array([+ const vertexPositionBuffer = new GLBuffer(gl, gl.ARRAY_BUFFER, new Float32Array([      ...createRect(-1, -1, 1, 2), // left rect      ...createRect(-1, 0, 1, 2), // right rect- ]);- const vertexPositionBuffer = gl.createBuffer();+ ]), gl.STATIC_DRAW);  - gl.bindBuffer(gl.ARRAY_BUFFER, vertexPositionBuffer);- gl.bufferData(gl.ARRAY_BUFFER, vertexPosition, gl.STATIC_DRAW);    const programInfo = setupShaderInput(gl, program, vShaderSource, fShaderSource);  + vertexPositionBuffer.bind(gl);  gl.vertexAttribPointer(programInfo.attributeLocations.position, 2, gl.FLOAT, false, 0, 0);    texCoordsBuffer.bind(gl);

还有索引缓冲区

???? src/texture.js

  texIndiciesBuffer.bind(gl);  gl.vertexAttribPointer(programInfo.attributeLocations.texIndex, 1, gl.FLOAT, false, 0, 0);  - const vertexIndices = new Uint8Array([+ const indexBuffer = new GLBuffer(gl, gl.ELEMENT_ARRAY_BUFFER, new Uint8Array([      // left rect      0, 1, 2,       1, 2, 3,       // right rect      4, 5, 6,       5, 6, 7,- ]);- const indexBuffer = gl.createBuffer();- - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);- gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, vertexIndices, gl.STATIC_DRAW);+ ]), gl.STATIC_DRAW);    Promise.all([      loadImage(textureImageSrc),        gl.uniform2fv(programInfo.uniformLocations.resolution, [canvas.width, canvas.height]);  -     gl.drawElements(gl.TRIANGLES, vertexIndices.length, gl.UNSIGNED_BYTE, 0);+     gl.drawElements(gl.TRIANGLES, indexBuffer.data.length, gl.UNSIGNED_BYTE, 0);  });

当初,咱们能够应用更少的代码来进步着色器的工作效率!

今天见????