58

頂点属性配列を有効にする範囲については完全には明確ではありません。頂点属性の数が異なるいくつかの異なるシェーダープログラムがあります。glEnableVertexAttribArray呼び出しはシェーダープログラムに対してローカルですか、それともグローバルですか?

現在、シェーダープログラムを作成するときに頂点属性配列を有効にしていて、無効にすることはありません。すべてが機能しているようですが、描画呼び出しの直前/直後に有効/無効にする必要があるようです。これに影響はありますか?

(私はWebGLを使用しているので、実際に話し合っていますgl.enableVertexAttribArray。オレンジ色の本であるOpenGL Shading Languageは、これらの呼び出しについてまったく情報がないことgl.disableVertexAttribArrayにも注意してください。)

4

4 に答える 4

47

頂点属性配列が有効になっている状態は、頂点配列オブジェクト (VAO) にバインドするか、グローバルにすることができます。

VAO を使用している場合、属性配列は VAO にカプセル化されているため、無効にしないでください。

ただし、グローバルな頂点属性配列が有効な状態では、それらを無効にする必要があります。有効なままにしておくと、OpenGL が無効なポインターにバインドされている可能性がある配列から読み取ろうとし、ポインターがクライアント アドレスにある場合にプログラムがクラッシュする可能性があるためです。または、バインドされた頂点バッファ オブジェクトの範囲外を指している場合は、OpenGL エラーを発生させます。

于 2012-09-14T15:56:58.977 に答える
15

WebGL は OpenGL と同じではありません。

WebGL では、属性にアタッチされたバッファーがあり、(a) 使用されている場合は描画呼び出しを満たすのに十分な大きさである、または (b) 使用されていない限り、配列を有効のままにしておくことが明示的に許可されます。

OpenGL ES 2.0 とは異なり、WebGL はクライアント側配列を許可しません。

証拠:

const gl = document.querySelector("canvas").getContext("webgl");

const vsUses2Attributes = `
attribute vec4 position;
attribute vec4 color;
  
varying vec4 v_color;

void main() {
  gl_Position = position;
  gl_PointSize = 20.0;
  v_color = color;
}
`;
const vsUses1Attribute = `
attribute vec4 position;
  
varying vec4 v_color;
  
void main() {
  gl_Position = position;
  gl_PointSize = 20.0;
  v_color = vec4(0,1,1,1);
}
`
const fs = `
precision mediump float;
varying vec4 v_color;

void main() {
  gl_FragColor = v_color;
}
`;

const program2Attribs = twgl.createProgram(gl, [vsUses2Attributes, fs]);
const program1Attrib  = twgl.createProgram(gl, [vsUses1Attribute, fs]);

function createBuffer(data) {
  const buf = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, buf);
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW);
  return buf;
}

const buffer3Points = createBuffer([
  -0.7, 0.5,
   0.0, 0.5,
   0.7, 0.5,
]);
const buffer3Colors = createBuffer([
  1, 0, 0, 1,
  0, 1, 0, 1,
  0, 0, 1, 1,
]);

const buffer9Points = createBuffer([
  -0.8, -0.5,
  -0.6, -0.5,
  -0.4, -0.5,
  -0.2, -0.5,
   0.0, -0.5,
   0.2, -0.5,
   0.4, -0.5,
   0.6, -0.5,
   0.8, -0.5,
]);

// set up 2 attributes
{
  const posLoc = gl.getAttribLocation(program2Attribs, 'position');
  gl.enableVertexAttribArray(posLoc);
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer3Points);
  gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0);

  const colorLoc = gl.getAttribLocation(program2Attribs, 'color');
  gl.enableVertexAttribArray(colorLoc);
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer3Colors);
  gl.vertexAttribPointer(colorLoc, 4, gl.FLOAT, false, 0, 0);
}

// draw
gl.useProgram(program2Attribs);
gl.drawArrays(gl.POINTS, 0, 3);

// setup 1 attribute (don't disable the second attribute
{
  const posLoc = gl.getAttribLocation(program1Attrib, 'position');
  gl.enableVertexAttribArray(posLoc);
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer9Points);
  gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0);
}

// draw
gl.useProgram(program1Attrib);
gl.drawArrays(gl.POINTS, 0, 9);
const err = gl.getError();
console.log(err ? `ERROR: ${twgl.glEnumToString(gl, err)}` : 'no WebGL errors');
canvas { border: 1px solid black; }
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<p>
1st it draws 3 points (3 vertices, 2 attributes)<br/>
2nd it draws 9 points (9 vertices, 1 attribute)<br/>
It does NOT call 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 9 vertices will be drawn. There are no errors.
</p>
<canvas></canvas>

別の例では、すべての属性を有効にしてから、属性を使用しないシェーダーで描画し (エラーなし)、1 つの属性を使用するシェーダーで描画します (これもエラーなし)。呼び出す必要はありません。gl.disbleVertexAttribArray

const gl = document.querySelector("canvas").getContext("webgl");

const vsUses1Attributes = `
attribute vec4 position;

void main() {
  gl_Position = position;
  gl_PointSize = 20.0;
}
`;
const vsUses0Attributes = `
void main() {
  gl_Position = vec4(0, 0, 0, 1);
  gl_PointSize = 20.0;
}
`
const fs = `
precision mediump float;
void main() {
  gl_FragColor = vec4(1, 0, 0, 1);
}
`;

const program0Attribs = twgl.createProgram(gl, [vsUses0Attributes, fs]);
const program1Attrib  = twgl.createProgram(gl, [vsUses1Attributes, fs]);

function createBuffer(data) {
  const buf = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, buf);
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW);
  return buf;
}

const buffer3Points = createBuffer([
  -0.7, 0.5,
   0.0, 0.5,
   0.7, 0.5,
]);

const buffer0Points = createBuffer([]);

// enable all the attributes and bind a buffer to them
const maxAttrib = gl.getParameter(gl.MAX_VERTEX_ATTRIBS);
for (let i = 0; i < maxAttrib; ++i) {
  gl.enableVertexAttribArray(i);
  gl.vertexAttribPointer(i, 2, gl.FLOAT, false, 0, 0);
}

gl.useProgram(program0Attribs);
gl.drawArrays(gl.POINTS, 0, 1);

gl.useProgram(program1Attrib);
const posLoc = gl.getAttribLocation(program1Attrib, 'position');
gl.bindBuffer(gl.ARRAY_BUFFER, buffer3Points);
gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0);
gl.drawArrays(gl.POINTS, 0, 3);

const err = gl.getError();
console.log(err ? `ERROR: ${twgl.glEnumToString(gl, err)}` : 'no WebGL errors');
canvas { border: 1px solid black; }
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<p>
1st it enables all attributes<br/>
2nd it draws 1 point that needs no attributes (no error)<br/>
3rd it draws 3 points that need 1 attribute (no error)<br/>
It does NOT call gl.disableVertexAttribArray on any of the attributes so they are all still enabled.  There are no errors.
</p>
<canvas></canvas>

于 2012-09-15T09:22:09.560 に答える
3

webGLの場合はyesを使用します。gl.disableVertexAttribArray を呼び出すことが重要です。

Chrome で次の警告が表示されました。

WebGL: INVALID_OPERATION: drawElements: attribs not setup correctly

これは、プログラムが最大数未満の属性を使用するプログラムに変更されたときに発生していました。明らかに解決策は、描画する前に未使用の属性を無効にすることでした。

すべてのプログラムが同じ数の属性を使用する場合、初期化時に gl.enableVertexAttribArray を 1 回呼び出すだけで済む場合があります。そうしないと、プログラムを変更するときにそれらを管理する必要があります。

于 2013-11-13T06:55:49.550 に答える