1. ホーム
  2. opengl

[解決済み] Emscripten/OpenGLのコードで「glDrawArrays: attribute 1の範囲外の頂点にアクセスしようとする」(ネイティブC++では動作する) [終了しました]

2022-02-05 05:36:27

質問

問題を絞り込むと、こうなります。 全く同じデータを指している属性が2つある。 これは、ネイティブC++でビルドした場合は正常に動作します。 しかし、emscriptenでビルドすると、javascriptコンソールに各フレームで次のエラーが表示されます。

'glDrawArrays: attempt to access out of range vertices in attribute 1'

glEnableVertexAttribArray」の行をコメントアウトして2つ目のアトリビュートを有効にすると、このエラーは発生しません。

以下は私のコードです。 まず、データバッファの作成から始めます。

GLfloat rectangleData[] =
{
    -.5f, -.5f,     0,1,
    -.5f, .5f,      0,0,
    .5f, .5f,       1,0,
    .5f, -.5f,      1,1,
    -.5f, -.5f,     0,1
};
glGenBuffers(1, &rectangleBuffer);
glBindBuffer(GL_ARRAY_BUFFER, rectangleBuffer);
glBufferData(
        GL_ARRAY_BUFFER, sizeof(rectangleData),
        rectangleData, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);

以下は、私のテクスチャ付き四角形描画コードからの関連する抜粋です。

glBindBuffer(GL_ARRAY_BUFFER, rectangleBuffer);

int vertexPosition = Shader::getParameterInfo("vertexPosition")->id;
glVertexAttribPointer(
        vertexPosition, 2, GL_FLOAT,
        GL_FALSE, 16, BUFFER_OFFSET(0));
glEnableVertexAttribArray(vertexPosition);

int vertexTexCoord = Shader::getParameterInfo("vertexTexCoord")->id;
glVertexAttribPointer(
        vertexTexCoord, 2, GL_FLOAT,
        GL_FALSE, 16, BUFFER_OFFSET(0));
glEnableVertexAttribArray(vertexTexCoord);

glDrawArrays(GL_TRIANGLE_FAN, 0, 5);

2番目の属性は、1番目の属性と同じデータを指すように調整していることに注意してください(デバッグ時の複雑さを軽減するため)。 私はここでかなり困っていて、新鮮で経験豊富な視点を本当に利用したいです。

編集:以下は BUFFER_OFFSET のように見えます。

#define BUFFER_OFFSET(i) ((char *)NULL + (i))

出典 int を const GLvoid* にキャストするには?

EDIT: 参考までに、同等のEmscriptenが生成したJSコードを以下に示します。 要求があれば、これが参照するJSコードも掲載します。

dest=$rectangleData; src=2328; stop=dest+80|0; do {
HEAP32[dest>>2]=HEAP32[src>>2]|0; dest=dest+4|0; src=src+4|0; } while
((dest|0) < (stop|0));
 _glGenBuffers(1,(2300|0));
 $30 = HEAP32[2300>>2]|0;
 _glBindBuffer(34962,($30|0));
 _glBufferData(34962,80,($rectangleData|0),35044);
 _glBindBuffer(34962,0);

$11 = HEAP32[2300>>2]|0;
 _glBindBuffer(34962,($11|0));
 $12 = (__ZN8platform6Shader16getParameterInfoEPKc(17356)|0);
 $13 = HEAP32[$12>>2]|0;
 $vertexPosition = $13;
 $14 = $vertexPosition;
 _glVertexAttribPointer(($14|0),2,5126,0,16,(0|0));
 $15 = $vertexPosition;
 _glEnableVertexAttribArray(($15|0));
 $16 = (__ZN8platform6Shader16getParameterInfoEPKc(17379)|0);
 $17 = HEAP32[$16>>2]|0;
 $vertexTexCoord = $17;
 $18 = $vertexTexCoord;
 _glVertexAttribPointer(($18|0),2,5126,0,16,(0|0));
 $19 = $vertexTexCoord;
 _glEnableVertexAttribArray(($19|0));
 _glDrawArrays(6,0,5);

Edit: もっといいのは、githubで動いているJSコードへのリンクと、C++コードも提供することです("drawImage()"の一番下付近にあります)。

https://rawgit.com/jon-heard/Native-WebGL-framework/c134e35ac94fdf3243a9662353ad2227f8c84b43/Native-WebGL-framework/web/index.html

https://github.com/jon-heard/Native-WebGL-framework/blob/c134e35ac94fdf3243a9662353ad2227f8c84b43/Native-WebGL-framework/src/platform/draw.cpp

解決方法は?

問題は、1つの頂点シェーダで 常に2つのアトリビュートを使用する

var gl = document.createElement("canvas").getContext("webgl");
var program = twgl.createProgramFromScripts(gl, ["vs", "fs"]);

log("list of used attributes");
log("-----------------------");

var numAttribs = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES);
for (var ii = 0; ii < numAttribs; ++ii) {
  var attribInfo = gl.getActiveAttrib(program, ii);
  if (!attribInfo) {
    break;
  }
  log(gl.getAttribLocation(program, attribInfo.name), attribInfo.name);
}

function log(...args) {
   var div = document.createElement("div");
   div.textContent = [...args].join(" ");
   document.body.appendChild(div);
}
<script src="https://twgljs.org/dist/2.x/twgl.min.js"></script>
<script type="foo" id="vs">
uniform mat4 sceneTransform;
uniform mat4 rotationTransform;
uniform vec2 objectPosition;
uniform vec2 objectScale;
attribute vec2 vertexPosition;
attribute vec2 vertexTexCoord;
varying vec2 UVs;
void main()
{
  UVs = vertexTexCoord;
  gl_Position = 
    sceneTransform *
    vec4( vertexPosition * objectScale + objectPosition, 0, 1);
}
</script>
<script type="foo" id="fs">
precision mediump float;

uniform vec3 objectColor;
uniform float objectOpacity;

void main()
{
	gl_FragColor = vec4(objectColor, objectOpacity);
}
</script>

drawCircleを呼び出すと、これらの属性の両方がバッファに割り当てられますが、上記のコードでは、2番目の属性について何もしなければ、前のバッファを指したままです。このバッファは描画の呼び出しに対して小さすぎるため、エラーになります。

WebGLは未使用の属性については文句を言いませんが、使用済みの属性については文句を言います。シェーダが必要とする属性は常に提供すべきです。

あなたの場合、少なくとも2つのオプションがあります。

  1. シェーダーが1つのアトリビュートしか使用しないようにコードを変更する。

    コードを正しく読めば、バーテックスシェーダーは1つだけですね。フラグメントシェーダがテクスチャ座標を使用しない場合、テクスチャ座標を提供しない別のバーテックスシェーダを使用します。

  2. 属性を無効にして、定数値を使用するようにします。

    gl.disableVertexAttribArray(...)
    
    

    が提供する定数値を使用することを意味します。

    gl.vertexAttribXXX
    
    

なぜなら、バーテックスシェーダはアトリビュートから読み込んで、それをフラグメントシェーダで使わないで、変化するものにコピーするという無駄な時間を過ごさないからです。