Когда мне следует включать / отключать атрибуты положения вершин в WebGL/OpenGL?
Я работаю над кодом WebGL, в котором несколько шейдерных программ работают последовательно.
Ранее я использовал gl.enableVertexAttribArray(...)
как требуется во время инициализации для моего контекста gl и шейдеров. Я предположил, возможно, неправильно, что вызов этой функции устанавливал состояние, специфичное для программы, выбранной gl.useProgram(...)
Теперь моя первая шейдерная программа имеет два включенных массива атрибутов, а вторая - один. Когда запускается вторая программа, я получаю сообщение об ошибке:
Error: WebGL: drawArrays: no VBO bound to enabled vertex attrib index 1!
Так что это заставляет меня думать, что, возможно, мне нужно отключить атрибут вершины 1 после использования его в первой программе, но я хотел убедиться, что это именно то, что я должен делать, и, надеюсь, получить объяснение, почему это это или не правильно.
Является ли лучшая практика для enableVertexAttribArray(...)
а также disableVertexAttribArray
для каждого расположения массива до и после каждого использования?
3 ответа
Этот вопрос помог мне ответить на мой вопрос:
Конфликт при использовании двух или более шейдеров с разным количеством атрибутов
Чтобы заставить его работать правильно, мне нужно было отключить неиспользуемые атрибуты перед рисованием с помощью шейдерной программы, которая их не использует. Я не могу объяснить, почему некоторые люди говорят, что в этом нет необходимости, но это решает мою проблему.
Я никогда не звонил disableVertexAttribArray
в моей жизни, и я написал сотни программ для WebGL. Называть его может быть, а может и не быть, но нет проблем с совместимостью, чтобы не вызывать его.
В спецификации сказано, что вы получите ошибку только в том случае, если атрибут используется текущей программой, и доступ будет вне диапазона или если нет буфера, связанного с включенным атрибутом.
Мы можем проверить это и увидеть, что это работает просто отлично.
var vsThatUses2Attributes = `
attribute vec4 position;
attribute vec2 texcoord;
varying vec2 v_texcoord;
void main() {
v_texcoord = texcoord;
gl_Position = position;
}
`;
var vsThatUses1Attribute = `
attribute vec4 position;
varying vec2 v_texcoord;
void main() {
v_texcoord = position.xy * 0.5 + 0.5;
gl_Position = position + vec4(1, 0, 0, 0);
}
`;
var fs = `
precision mediump float;
varying vec2 v_texcoord;
void main () {
gl_FragColor = vec4(v_texcoord, v_texcoord.x * v_texcoord.y, 1);
}
`;
var gl = document.querySelector("canvas").getContext("webgl");
document.body.appendChild(gl.canvas);
var programThatUses2Attributes = twgl.createProgramFromSources(gl, [vsThatUses2Attributes, fs]);
var programThatUses1Attribute = twgl.createProgramFromSources(gl, [vsThatUses1Attribute, fs]);
var positionLocation2AttribProg = gl.getAttribLocation(programThatUses2Attributes, "position");
var texcoordLocation2AttribProg = gl.getAttribLocation(programThatUses2Attributes, "texcoord");
var positionLocation1AttribProg = gl.getAttribLocation(programThatUses1Attribute, "position");
var positionBufferFor2AttribPrg = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBufferFor2AttribPrg);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
-1, -1,
-.5, 1,
0, -1,
]), gl.STATIC_DRAW);
var texcoordBufferFor2AttribPrg = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBufferFor2AttribPrg);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
0, 0,
0.5, 1,
1, 0,
]), gl.STATIC_DRAW);
var positionBufferFor1AttribPrg = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBufferFor1AttribPrg);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
-1, -1,
0, -1,
-1, 1,
-1, 1,
0, -1,
0, 1,
]), gl.STATIC_DRAW);
// turn on 2 attributes
gl.enableVertexAttribArray(positionLocation2AttribProg);
gl.enableVertexAttribArray(texcoordLocation2AttribProg);
gl.bindBuffer(gl.ARRAY_BUFFER, positionBufferFor2AttribPrg);
gl.vertexAttribPointer(positionLocation2AttribProg, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBufferFor2AttribPrg);
gl.vertexAttribPointer(texcoordLocation2AttribProg, 2, gl.FLOAT, false, 0, 0);
// draw with 2 attributes enabled
gl.useProgram(programThatUses2Attributes);
gl.drawArrays(gl.TRIANGLES, 0, 3);
// setup for second program that uses only 1 attribute
gl.bindBuffer(gl.ARRAY_BUFFER, positionBufferFor1AttribPrg);
gl.vertexAttribPointer(positionLocation1AttribProg, 2, gl.FLOAT, false, 0, 0);
// NOTICE WE HAVE !NOT turned off other attribute
gl.useProgram(programThatUses1Attribute);
gl.drawArrays(gl.TRIANGLES, 0, 6);
log("glError:", gl.getError());
function log() {
var pre = document.createElement("pre");
pre.appendChild(document.createTextNode(Array.prototype.join.call(arguments, " ")));
document.body.appendChild(pre);
}
canvas { border: 1px solid black; }
<script src="https://twgljs.org/dist/twgl.min.js"></script>
<pre>
This example draws a triangle with 3 vertices using 2 attributes.
It then draws a quad using 6 vertices and 1 attribute
<b>WITHOUT TURNING OFF THE NOW 2nd UNUSED ATTRIBUTE</b>.
That means not only is that attribute left on but it only has 3 vertices even
though the draw will use 6 vertices. Because that attribute is not 'comsumed' by
the current program it's ok according to the spec.
</pre>
<canvas width="150" height="30"></canvas>
Таким образом, ваша ошибка, вероятно, что-то еще.
Обратите внимание, что удаление буфера отвяжет его от атрибута, после чего он станет включенным атрибутом без буфера и вызовет ошибку, если вы не отключите его.
Состояние атрибута отделено от состояния программы, которое вы обнаружили.
Ваша ошибка означает именно то, что она говорит. Вы пытались нарисовать, этой программе требуются данные по атрибуту #1. Вы включили его в какой-то момент с gl.enableVertexAttribArray
но вы не дали ему никаких данных с gl.vertexAttribPointer
, Итак, вы получили ошибку.
Обратите внимание, что gl.vertexAttribPointer
связывает буфер, в настоящее время связанный с gl.ARRAY_BUFFER
к указанному атрибуту.
Вы можете найти этот ответ полезным
/questions/16541082/kakova-logika-svyazyivaniya-buferov-v-webgl/16541084#16541084
Вам никогда не потребуется вызывать disableVertexAttribArray в WebGL. Я отмечу, почему этот метод действительно существует сначала.
Как вы знаете, WebGL был просто портированной библиотекой из OpenGLES2, а OpenGLES2 является подмножеством OpenGL. В списке API OpenGL есть и другой способ передачи буферов вершин в графический процессор без указания буфера вершин в качестве указателя. Например, в среде OpenGL вы можете отправлять данные буфера вершин, вызывая gl.begin,gl.Vertex,gl.end и так далее. Таким образом, вам не нужно вызывать gl.enableVertexAttribArray.
Но нет способа указать буферы без вызова eableVertexAttribArray в WebGL. Поэтому вам никогда не нужно вызывать метод, это просто историческая причина.
И вам не нужно вызывать enableVertexAttribArray в каждом кадре. Это очень тяжелая операция, так как не следует называть это каждым кадром. Вам просто нужно вызвать enableVertexAttribArray сразу после инициализации буфера.