乐趣区

关于前端:WebGL-系列-11-减少样板代码

  • 原文地址: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);
  });

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

今天见????

退出移动版