Унифицированный буфер Объекты - это просто другой способ установки униформы. Они не помогают с экземплярами. Это просто способ «потенциально» настроить форму быстрее, чем использовать gl.uniformXXX
. Например, если у вас есть шейдер, который принимает ambient
, diffuse
, specular
, shininess
в качестве входных данных, вы можете создать унифицированный блок, содержащий эти 4 униформы. Затем вы можете создать один единый буферный объект для каждого материала, все 4 из этих настроек, вы можете установить разные материалы с помощью одной функции WebGL
gl.bindBufferRange(gl.UNIFORM_BUFFER, blockIndex, materialBuffer, [offset], [length]);
Вместо 4 вызовов WebGL
gl.uniform3fv(ambientLocation, ...);
gl.uniform3fv(diffuseLocation, ...);
gl.uniform3fv(specularLocation, ...);
gl.uniform1f(shininessLocation, ...);
Таким образом, объекты единого буфера не помогают в создании экземпляров. Все, что они делают, это помогают с настройкой информации, возможно, быстрее. Я говорю «возможно», потому что, хотя я подозреваю, что при выполнении вышеизложенного, предварительное создание во время инициализации единого буферного объекта и во время рендеринга привязка его к блоку Uniforn, безусловно, быстрее, чем вызов gl.uniform
4 раза. С другой стороны, если значения униформ меняются в каждом кадре, необходимо обновить буфер, вызвав gl.bufferSubData
с новыми значениями. Это может быть или не быть быстрее, чем просто позвонить gl.uniform
. Я не профилировал это. В WebGL больше накладных расходов, чем в OpenGL. Это, вероятно, быстрее, чем больше униформ у вас есть.
В этом ответе показан пример использования унифицированных буферных объектов.
В любом случае важно отметить, что они просто быстрееспособ установки формы. Они не поддерживают новую функциональность.
Instanced Drawing действительно предоставляет новые функциональные возможности. Возможность рисовать несколько вещей одним вызовом отрисовки и обновлять некоторые из ваших атрибутов только один раз для каждого экземпляра, а не один раз для каждой вершины, как они обычно делают.
Итак, чтобы использовать экземплярное рисование, вы обычно должны установитьдо некоторого атрибута с данными, которые будут меняться один раз за экземпляр. Наиболее очевидным является атрибут mat4
для предоставления другой матрицы для каждого экземпляра. Конечно, если вы хотите использовать разные цвета для каждого экземпляра, вам нужно будет также предоставить атрибуты для цветов.
Любой атрибут, который вы хотите изменить только один раз для экземпляра, а не один раз для каждой вершины, как обычно, вы вызываете
gl.vertexAttribDivisor(attributeLocation, 1);
1 в приведенном выше утверждении означает, что только атрибут 1 переходит к следующему значению каждый 1 экземпляр. Помещение 2 означает, что атрибут продвигается только после 2-х экземпляров и т. Д. 0 = делать обычные вещи и продвигать каждую вершину.
Пример:
const m4 = twgl.m4;
const gl = document.querySelector('canvas').getContext('webgl2');
if (!gl) alert('need WebGL2');
const positionLoc = 0;
const matrixLoc = 1; // note: mat4 attributes take 4 indices
const colorLoc = 5;
const vs = `#version 300 es
layout(location = ${positionLoc}) in vec4 position;
layout(location = ${matrixLoc}) in mat4 modelMatrix; // we'll make this per instance
layout(location = ${colorLoc}) in vec4 color; // we'll make this per instance
uniform mat4 viewProjection;
out vec4 v_color;
void main () {
gl_Position = viewProjection * modelMatrix * position;
v_color = color;
}
`;
const fs = `#version 300 es
precision mediump float;
in vec4 v_color;
out vec4 outColor;
void main() {
outColor = v_color;
}
`;
const program = twgl.createProgram(gl, [vs, fs]);
const viewProjectionLoc = gl.getUniformLocation(program, 'viewProjection');
const vao = gl.createVertexArray();
gl.bindVertexArray(vao);
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
0, 0.2,
-0.2, -0.2,
0.2, -0.2,
]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(positionLoc);
gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0);
const deg = v => v * Math.PI / 180;
// setup matrixes, one per instance
const matrixBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, matrixBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
...m4.rotateZ(m4.translation([-0.5, 0, 0]), deg(10)),
...m4.rotateZ(m4.translation([-0.25, 0, 0]), deg(20)),
...m4.rotateZ(m4.translation([0, 0, 0]), deg(30)),
...m4.rotateZ(m4.translation([0.25, 0, 0]), deg(40)),
...m4.rotateZ(m4.translation([0.5, 0, 0]), deg(50)),
]), gl.DYNAMIC_DRAW);
// set all 4 attributes for matrix
for (let i = 0; i < 4; ++i) {
gl.enableVertexAttribArray(matrixLoc + i);
// note the stride and offset
gl.vertexAttribPointer(matrixLoc + i, 4, gl.FLOAT, false, 64, i * 16);
// this line says this attribute only changes for each 1 instance
gl.vertexAttribDivisor(matrixLoc + i, 1);
}
// setup colors, one per instance
const colorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
1, 0, 0, 1,
0, 1, 0, 1,
0, 0, 1, 1,
1, 0, 1, 1,
0, 1, 1, 1,
]), gl.DYNAMIC_DRAW);
// set all attribute for color
gl.enableVertexAttribArray(colorLoc);
gl.vertexAttribPointer(colorLoc, 4, gl.FLOAT, false, 0, 0);
// this line says this attribute only changes for each 1 instance
gl.vertexAttribDivisor(colorLoc, 1);
gl.useProgram(program);
gl.uniformMatrix4fv(viewProjectionLoc, false, m4.identity());
gl.drawArraysInstanced(
gl.TRIANGLES,
0, // offset
3, // num vertices per instance
5, // num instances
);
canvas { border: 1px solid black; }
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>
Для их анимации вам необходимо обновить данные буфера для matrixBuffer
каждого кадра, вызвав gl.bufferSubData
const m4 = twgl.m4;
const gl = document.querySelector('canvas').getContext('webgl2');
if (!gl) alert('need WebGL2');
const positionLoc = 0;
const matrixLoc = 1; // note: mat4 attributes take 4 indices
const colorLoc = 5;
const vs = `#version 300 es
layout(location = ${positionLoc}) in vec4 position;
layout(location = ${matrixLoc}) in mat4 modelMatrix; // we'll make this per instance
layout(location = ${colorLoc}) in vec4 color; // we'll make this per instance
uniform mat4 viewProjection;
out vec4 v_color;
void main () {
gl_Position = viewProjection * modelMatrix * position;
v_color = color;
}
`;
const fs = `#version 300 es
precision mediump float;
in vec4 v_color;
out vec4 outColor;
void main() {
outColor = v_color;
}
`;
const program = twgl.createProgram(gl, [vs, fs]);
const viewProjectionLoc = gl.getUniformLocation(program, 'viewProjection');
const vao = gl.createVertexArray();
gl.bindVertexArray(vao);
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
0, 0.2,
-0.2, -0.2,
0.2, -0.2,
]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(positionLoc);
gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0);
const deg = v => v * Math.PI / 180;
// setup matrixes, one per instance
const numInstances = 5;
const matrixBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, matrixBuffer);
// just allocate the buffer
gl.bufferData(gl.ARRAY_BUFFER, numInstances * 16 * 4, gl.DYNAMIC_DRAW);
// set all 4 attributes for matrix
for (let i = 0; i < 4; ++i) {
gl.enableVertexAttribArray(matrixLoc + i);
// note the stride and offset
gl.vertexAttribPointer(matrixLoc + i, 4, gl.FLOAT, false, 64, i * 16);
// this line says this attribute only changes for each 1 instance
gl.vertexAttribDivisor(matrixLoc + i, 1);
}
// setup colors, one per instance
const colorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
1, 0, 0, 1,
0, 1, 0, 1,
0, 0, 1, 1,
1, 0, 1, 1,
0, 1, 1, 1,
]), gl.DYNAMIC_DRAW);
// set all attribute for color
gl.enableVertexAttribArray(colorLoc);
gl.vertexAttribPointer(colorLoc, 4, gl.FLOAT, false, 0, 0);
// this line says this attribute only changes for each 1 instance
gl.vertexAttribDivisor(colorLoc, 1);
// make a typed array with one view per matrix
const matrixData = new Float32Array(numInstances * 16);
const matrices = [];
for (let i = 0; i < numInstances; ++i) {
matrices.push(new Float32Array(matrixData.buffer, i * 16 * 4, 16));
}
function render(time) {
time *= 0.001; // seconds
// update all the matrices
matrices.forEach((mat, ndx) => {
m4.translation([-0.5 + ndx * 0.25, 0, 0], mat);
m4.rotateZ(mat, time * (0.1 + 0.1 * ndx), mat);
});
// upload the new matrix data
gl.bindBuffer(gl.ARRAY_BUFFER, matrixBuffer);
gl.bufferSubData(gl.ARRAY_BUFFER, 0, matrixData);
gl.bindVertexArray(vao);
gl.useProgram(program);
gl.uniformMatrix4fv(viewProjectionLoc, false, m4.identity());
gl.drawArraysInstanced(
gl.TRIANGLES,
0, // offset
3, // num vertices per instance
numInstances, // num instances
);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
canvas { border: 1px solid black; }
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>