共计 9154 个字符,预计需要花费 23 分钟才能阅读完成。
- 原文地址: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);
});
当初,咱们能够应用更少的代码来进步着色器的工作效率!
今天见????