Важно ли вызывать glDisableVertexAttribArray()?
Я не совсем понимаю, как включить массивы атрибутов вершин. У меня есть несколько разных шейдерных программ с разным количеством атрибутов вершин. Являются glEnableVertexAttribArray
вызывает локальную шейдерную программу или глобальную?
Прямо сейчас я включаю массивы атрибутов вершин, когда создаю шейдерную программу, и никогда не отключаю их, и все, кажется, работает, но кажется, что я, вероятно, должен включать / отключать их прямо до / после вызовов отрисовки. Есть ли влияние на это?
(Я в WebGL, как это бывает, поэтому мы действительно говорим о gl.enableVertexAttribArray
а также gl.disableVertexAttribArray
, Отмечу также, что оранжевая книга OpenGL Shading Language довольно неинформативна в отношении этих вызовов.)
3 ответа
Состояние, в котором включены массивы атрибутов вершин, может быть связано с объектом массива вершин (VAO) или быть глобальным.
Если вы используете VAO, вам не следует отключать массивы атрибутов, поскольку они инкапсулированы в VAO.
Однако для состояния включенного массива глобальных атрибутов вершин вы должны отключить их, потому что если они оставлены включенными, OpenGL будет пытаться читать из массивов, которые могут быть связаны с недопустимым указателем, что может привести к сбою вашей программы, если указатель на адрес клиента пробел или вызовите ошибку OpenGL, если она выходит за пределы связанного объекта буфера вершин.
WebGL - это не то же самое, что OpenGL.
В WebGL оставлять включенные массивы явно разрешено, если к атрибуту присоединен буфер, и (a) если он используется, он достаточно большой, чтобы удовлетворить вызов отрисовки, или (b) он не используется.
В отличие от OpenGL ES 2.0, WebGL не поддерживает массивы на стороне клиента.
Доказательство:
var gl = document.querySelector("canvas").getContext("webgl");
var m4 = twgl.m4;
var programInfo2Attribs = twgl.createProgramInfo(gl, ["vs-uses-2-attributes", "fs"]);
var programInfo1Attrib = twgl.createProgramInfo(gl, ["vs-uses-1-attribute", "fs"]);
var arrays2Attribs = {
position: [
-1, -1, 0,
1, -1, 0,
-1, 1, 0,
],
color: [
1,0,0,1,
1,1,0,1,
0,1,0,1,
],
};
var arrays1Attrib = {
position: [
-1, -1, 0,
1, -1, 0,
-1, 1, 0,
-1, 1, 0,
1, -1, 0,
1, 1, 0,
],
};
var bufferInfo2Attribs = twgl.createBufferInfoFromArrays(gl, arrays2Attribs);
var bufferInfo1Attrib = twgl.createBufferInfoFromArrays(gl, arrays1Attrib);
var uniforms = {
u_matrix: m4.scale(m4.translation([-0.5, 0, 0]), [0.25, 0.5, 0.5]),
};
gl.useProgram(programInfo2Attribs.program);
twgl.setBuffersAndAttributes(gl, programInfo2Attribs, bufferInfo2Attribs);
twgl.setUniforms(programInfo2Attribs, uniforms);
twgl.drawBufferInfo(gl, gl.TRIANGLES, bufferInfo2Attribs);
uniforms.u_matrix = m4.scale(m4.translation([0.5, 0, 0]), [0.25, 0.5, 0.5]);
gl.useProgram(programInfo1Attrib.program);
twgl.setBuffersAndAttributes(gl, programInfo1Attrib, bufferInfo1Attrib);
twgl.setUniforms(programInfo1Attrib, uniforms);
twgl.drawBufferInfo(gl, gl.TRIANGLES, bufferInfo1Attrib);
canvas { border: 1px solid black; }
<script src="https://twgljs.org/dist/twgl-full.min.js"></script>
<script id="vs-uses-2-attributes" type="not-js">
attribute vec4 position;
attribute vec4 color;
varying vec4 v_color;
uniform mat4 u_matrix;
void main() {
gl_Position = u_matrix * position;
v_color = color;
}
</script>
<script id="vs-uses-1-attribute" type="not-js">
attribute vec4 position;
varying vec4 v_color;
uniform mat4 u_matrix;
void main() {
gl_Position = u_matrix * position;
v_color = vec4(0,1,0,1);
}
</script>
<script id="fs" type="not-js">
precision mediump float;
varying vec4 v_color;
void main() {
gl_FragColor = v_color;
}
</script>
<p>
1st it draws a triangle (3 vertices, 2 attributes)<br/>
2nd it draws a quad (6 vertices, 1 attribute)<br/>
It does NOT called gl.disableVertexAttrib so on the second draw call one of the attributes is still enabled. It is pointing to a buffer with only 3 vertices in it even though 6 vertices will be drawn. There are no errors.
</p>
<canvas></canvas>
Для webGL я собираюсь пойти с да, важно вызвать gl.disableVertexAttribArray.
Хром дал мне это предупреждение:
WebGL: INVALID_OPERATION: drawElements: attribs not setup correctly
Это происходило, когда программа менялась на один, использующий меньше максимального количества атрибутов. Очевидно, что решением было отключить неиспользуемые атрибуты перед рисованием.
Если все ваши программы используют одинаковое количество атрибутов, вы можете избежать вызова gl.enableVertexAttribArray один раз при инициализации. В противном случае вам придется управлять ими при смене программ.
Думайте об этом как об атрибутах, которые являются локальными для VAO, а не шейдерной программы. VBO находятся в памяти графического процессора.
Теперь представьте, что в WebGL есть VAO по умолчанию, который WebGL использует по умолчанию. (это также может быть VAO, созданный программистом, применяется та же концепция). Этот VAO содержит цель под названием ARRAY_BUFFER, к которой может привязаться любой VBO в памяти GPU. Этот VAO также содержит массив атрибутов с фиксированным количеством слотов атрибутов (количество зависит от реализации и платформы, здесь допустим 8, что является минимумом, требуемым спецификацией Webgl). Кроме того, этот VAO будет иметь цель ELEMENT_ARRAY_BUFFER, к которой может быть привязан любой буфер данных индекса.
Теперь, когда вы создаете программу шейдера, она будет иметь указанные вами атрибуты. Webgl присвоит один из возможных "номеров" слота атрибута всем атрибутам, указанным в программе, когда вы связываете программу шейдера. Теперь атрибуты будут использовать соответствующие слоты атрибутов в VAO для доступа к данным, привязанным к целям ARRAY_BUFFER или ELEMENT_ARRAY_BUFFER в VAO. Теперь, когда вы используете функции gl.enableVertexAttribArray(location) и gl.vertexAttribPointer(location,....), вы не меняете никаких характеристик атрибутов в программе шейдера (у них просто есть номер атрибута, который относится к одному из слоты атрибутов в VAO, которые они будут использовать для доступа к данным). Фактически вы изменяете состояние слота атрибута в VAO, используя его номер местоположения.Итак, чтобы атрибуты в программе могли получить доступ к данным, соответствующий слот атрибутов в VAO должен быть включен (gl.enableVertexAttribArray()). И мы должны настроить слот атрибута, чтобы он мог правильно читать данные из буфера, привязанного к ARRAY_BUFFER (gl.vertexAttribPointer()). один VBO установлен для слота, который он не изменит, даже если мы отвяжем его от цели, слот атрибута csn по-прежнему красный от VBO, пока он находится в памяти GPU. Кроме того, должен быть некоторый буфер, привязанный к целям VAO (gl.bindBuffer()). Таким образом, gl.enableVertexAttribArray(location) включит слот атрибута, указанный параметром location в текущем VAO. gl.disableVertexAttribArray(location) отключит его. Однако это не имеет ничего общего с программой шейдеров. Даже если вы используете другую программу шейдеров,состояние этих слотов атрибутов не изменится.
Итак, если две разные программы шейдеров используют одни и те же слоты атрибутов, ошибки не будет, потому что соответствующие слоты атрибутов в VAO уже активны. но данные из целевых объектов могут быть прочитаны неправильно, если требуются атрибуты по-разному интерпретируют данные в двух программах шейдера. Теперь подумайте, если две программы шейдеров используют разные слоты атрибутов, тогда вы можете включить требуемые слоты атрибутов второй программы шейдеров и подумать, что ваша программа должна работать. Но уже включенные слоты атрибутов (которые были включены предыдущей программой шейдера) будут по-прежнему включены, но не будут использоваться. Это вызывает ошибку.
Поэтому при изменении программ шейдера мы должны убедиться, что разрешенные слоты атрибутов в VAO, которые не будут использоваться этой программой шейдера, должны быть отключены. Хотя теперь мы можем указать любые VAO явно, Webgl по умолчанию работает так.
Один из способов - поддерживать список включенных атрибутов на стороне javascript и отключать все включенные слоты атрибутов при переключении программы при использовании того же VAO. Другой способ справиться с этой проблемой - создать настраиваемые VAO, к которым обращается только одна программа шейдера. но он менее эффективен. Еще один способ - привязать расположение атрибутов к фиксированным слотам до того, как программа шейдера будет связана с помощью gl.bindAttribLocation().