- 原文地址:Day 11. Reducing WebGL boilerplate
- 原文作者:Andrei Lesnitsky
这是 WebGL 系列的第 11 天教程,每天都有新文章公布。
订阅或者退出邮件列表以便及时获取更新内容。
源代码在这里
第 10 天咱们曾经学习了如何应用多个纹理。这须要批改着色器以及 JavaScript 代码,而且可能会有局部主动实现更改。
有一个软件包 glsl-extract-sync 能够获取无关着色器 attributes
和 uniforms
。
装置此软件包:
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" } }
当初,让咱们创立一个辅助函数,该函数将在软件包的帮忙下获取对 attributes
和 uniforms
的所有援用。
???? 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;+ }, {}); }
最初返回 attribute
和 uniform
地位
???? 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); });
当初,咱们能够应用更少的代码来进步着色器的工作效率!
今天见????