私は、OpenGLとGLSLを使用して、GPU Gems 3の最初の章で詳述したものと同様に、マーチングキューブアルゴリズムの2パスGPU実装を記述しようと試みました。ただし、最初のパスでのglDrawArrays
の呼び出しは、一貫して_GL_INVALID_OPERATION
_で失敗します。
私が見つけることができるすべてのドキュメントを検索し、glDrawArrays
がそのエラーをスローできる条件を見つけました。
GL_INVALID_OPERATION
_バインディングにバインドされ、バッファオブジェクトのデータストアが現在マップされている場合、_GL_DRAW_INDIRECT_BUFFER
_が生成されます。glDrawArrays
と対応するglBegin
の実行の間にglEnd
が実行されると、_GL_INVALID_OPERATION
_が生成されます。GL_INVALID_OPERATION
_は、現在のプログラムオブジェクト内の2つのアクティブサンプラーのタイプが異なるが、同じテクスチャイメージユニットを参照している場合、glDrawArrays
またはglDrawElements
によって生成されます。GL_INVALID_OPERATION
_が生成されます。GL_INVALID_OPERATION
_で、テッセレーションコントロールシェーダーがアクティブでない場合、_GL_PATCHES
_が生成されます。GL_INVALID_OPERATION
_は、プリミティブの頂点を変換フィードバック目的で使用されているバッファーオブジェクトに記録すると、バッファーオブジェクトのサイズの制限を超えるか、終了位置のオフセット+サイズ-1を超える場合に生成されますglBindBufferRange
によって設定されます。GL_INVALID_OPERATION
_はglDrawArrays()
によって生成されます。ジオメトリシェーダーが存在しない場合、変換フィードバックはアクティブであり、モードは許可されたモードの1つではありません。GL_INVALID_OPERATION
_は、glDrawArrays()
によって生成されます。ジオメトリシェーダーが存在し、変換フィードバックがアクティブで、ジオメトリシェーダーの出力プリミティブタイプが変換フィードバックPrimitiveModeと一致しない場合。GL_INVALID_OPERATION
_が生成されます。GL_INVALID_OPERATION
_は、変換フィードバックが使用されており、変換フィードバックバインディングポイントにバインドされたバッファーがまた、配列バッファのバインディングポイントにバインドされます。 これは私が抱えていた問題で、バッファがバインドされているタイプミスによるものです。仕様にはこれは違法であると記載されていますが、私が見つけたドキュメントでは、エラーをスローする理由の1つとしてglDrawArraysの下にリストされていません。残念ながら、私が見つけることができる公式文書のどれも、これらの3つ以上をカバーしていません。多数のソースからこのリストを収集する必要がありました。ポイント7と8は、実際にはglBeginTransformFeedback
のドキュメントに由来し、ポイント9はまったくドキュメント化されていないようです。どこかのフォーラムの投稿で言及されていることがわかりました。ただし、これらのどれもが私が得ているエラーを説明していないように見えるので、私はまだこのリストが完全だとは思わない。
glBegin
とglEnd
は使用できません。layout (points) in
であり、glDrawArrays
が_GL_POINTS
_で呼び出されています。GL_PATCHES
_または任意の種類のテッセレーションシェーダーを使用していません。layout (points) out
であり、glBeginTransformFeedback
が_GL_POINTS
_で呼び出されます。glValidateProgram
への呼び出しの直前にglDrawArrays
への呼び出しを挿入しようとすると、_GL_TRUE
_が返されました。実際のOpenGLコードは次のとおりです。
_ const int SECTOR_SIZE = 32;
const int SECTOR_SIZE_CUBED = SECTOR_SIZE * SECTOR_SIZE * SECTOR_SIZE;
const int CACHE_SIZE = SECTOR_SIZE + 3;
const int CACHE_SIZE_CUBED = CACHE_SIZE * CACHE_SIZE * CACHE_SIZE;
MarchingCubesDoublePass::MarchingCubesDoublePass(ServiceProvider* svc, DensityMap* sourceData) {
this->sourceData = sourceData;
densityCache = new float[CACHE_SIZE_CUBED];
}
MarchingCubesDoublePass::~MarchingCubesDoublePass() {
delete densityCache;
}
void MarchingCubesDoublePass::InitShaders() {
ShaderInfo vertShader, geoShader, fragShader;
vertShader = svc->shader->Load("data/shaders/MarchingCubesDoublePass-Pass1.vert", GL_VERTEX_SHADER);
svc->shader->Compile(vertShader);
geoShader = svc->shader->Load("data/shaders/MarchingCubesDoublePass-Pass1.geo", GL_GEOMETRY_SHADER);
svc->shader->Compile(geoShader);
shaderPass1 = glCreateProgram();
static const char* outputVaryings[] = { "triangle" };
glTransformFeedbackVaryings(shaderPass1, 1, outputVaryings, GL_SEPARATE_ATTRIBS);
assert(svc->shader->Link(shaderPass1, vertShader, geoShader));
uniPass1DensityMap = glGetUniformLocation(shaderPass1, "densityMap");
uniPass1TriTable = glGetUniformLocation(shaderPass1, "triangleTable");
uniPass1Size = glGetUniformLocation(shaderPass1, "size");
attribPass1VertPosition = glGetAttribLocation(shaderPass1, "vertPosition");
vertShader = svc->shader->Load("data/shaders/MarchingCubesDoublePass-Pass2.vert", GL_VERTEX_SHADER);
svc->shader->Compile(vertShader);
geoShader = svc->shader->Load("data/shaders/MarchingCubesDoublePass-Pass2.geo", GL_GEOMETRY_SHADER);
svc->shader->Compile(geoShader);
fragShader = svc->shader->Load("data/shaders/MarchingCubesDoublePass-Pass2.frag", GL_FRAGMENT_SHADER);
svc->shader->Compile(fragShader);
shaderPass2 = glCreateProgram();
assert(svc->shader->Link(shaderPass2, vertShader, geoShader, fragShader));
uniPass2DensityMap = glGetUniformLocation(shaderPass2, "densityMap");
uniPass2Size = glGetUniformLocation(shaderPass2, "size");
uniPass2Offset = glGetUniformLocation(shaderPass2, "offset");
uniPass2Matrix = glGetUniformLocation(shaderPass2, "matrix");
attribPass2Triangle = glGetAttribLocation(shaderPass2, "triangle");
}
void MarchingCubesDoublePass::InitTextures() {
for (int x = 0; x < CACHE_SIZE; x++) {
for (int y = 0; y < CACHE_SIZE; y++) {
for (int z = 0; z < CACHE_SIZE; z++) {
densityCache[x + y*CACHE_SIZE + z*CACHE_SIZE*CACHE_SIZE] = sourceData->GetDensity(Vector3(x-1, y-1, z-1));
}
}
}
glGenTextures(1, &densityTex);
glBindTexture(GL_TEXTURE_3D, densityTex);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_Edge);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_Edge);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_Edge);
glTexImage3D(GL_TEXTURE_3D, 0, GL_R32F, CACHE_SIZE, CACHE_SIZE, CACHE_SIZE, 0, GL_RED, GL_FLOAT, densityCache);
glGenTextures(1, &triTableTex);
glBindTexture(GL_TEXTURE_RECTANGLE, triTableTex);
glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_Edge);
glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_Edge);
glTexImage2D(GL_TEXTURE_RECTANGLE, 0, GL_R16I, 16, 256, 0, GL_RED_INTEGER, GL_INT, triTable);
}
void MarchingCubesDoublePass::InitBuffers() {
float* voxelGrid = new float[SECTOR_SIZE_CUBED*3];
unsigned int index = 0;
for (int x = 0; x < SECTOR_SIZE; x++) {
for (int y = 0; y < SECTOR_SIZE; y++) {
for (int z = 0; z < SECTOR_SIZE; z++) {
voxelGrid[index*3 + 0] = x;
voxelGrid[index*3 + 1] = y;
voxelGrid[index*3 + 2] = z;
index++;
}
}
}
glGenBuffers(1, &bufferPass1);
glBindBuffer(GL_ARRAY_BUFFER, bufferPass1);
glBufferData(GL_ARRAY_BUFFER, SECTOR_SIZE_CUBED*3*sizeof(float), voxelGrid, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glGenBuffers(1, &bufferPass2);
glBindBuffer(GL_ARRAY_BUFFER, bufferPass2);
glBufferData(GL_ARRAY_BUFFER, SECTOR_SIZE_CUBED*5*sizeof(int), NULL, GL_DYNAMIC_COPY);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glGenVertexArrays(1, &vaoPass1);
glBindVertexArray(vaoPass1);
glBindBuffer(GL_ARRAY_BUFFER, bufferPass1);
glVertexAttribPointer(attribPass1VertPosition, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glEnableVertexAttribArray(attribPass1VertPosition);
glBindVertexArray(0);
glGenVertexArrays(1, &vaoPass2);
glBindVertexArray(vaoPass2);
glBindBuffer(GL_ARRAY_BUFFER, bufferPass2);
glVertexAttribIPointer(attribPass2Triangle, 1, GL_INT, 0, (void*)0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glEnableVertexAttribArray(attribPass2Triangle);
glBindVertexArray(0);
glGenQueries(1, &queryNumTriangles);
}
void MarchingCubesDoublePass::Register(Genesis::ServiceProvider* svc, Genesis::Entity* ent) {
this->svc = svc;
this->ent = ent;
svc->scene->RegisterEntity(ent);
InitShaders();
InitTextures();
InitBuffers();
}
void MarchingCubesDoublePass::Unregister() {
if (!ent->GetBehavior<Genesis::Render>()) {
svc->scene->UnregisterEntity(ent);
}
}
void MarchingCubesDoublePass::RenderPass1() {
glEnable(GL_RASTERIZER_DISCARD);
glUseProgram(shaderPass1);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_3D, densityTex);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_RECTANGLE, triTableTex);
glUniform1i(uniPass1DensityMap, 0);
glUniform1i(uniPass1TriTable, 1);
glUniform1i(uniPass1Size, SECTOR_SIZE);
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, bufferPass2);
glBindVertexArray(vaoPass2);
glBeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, queryNumTriangles);
glBeginTransformFeedback(GL_POINTS);
GLenum error = glGetError();
glDrawArrays(GL_POINTS, 0, SECTOR_SIZE_CUBED);
error = glGetError();
glEndTransformFeedback();
glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN);
glBindVertexArray(0);
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0);
glUseProgram(0);
glDisable(GL_RASTERIZER_DISCARD);
glGetQueryObjectuiv(queryNumTriangles, GL_QUERY_RESULT, &numTriangles);
}
void MarchingCubesDoublePass::RenderPass2(Matrix mat) {
glUseProgram(shaderPass2);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_3D, densityTex);
glUniform1i(uniPass2DensityMap, 0);
glUniform1i(uniPass2Size, SECTOR_SIZE);
glUniform3f(uniPass2Offset, 0, 0, 0);
mat.UniformMatrix(uniPass2Matrix);
glBindVertexArray(vaoPass2);
glDrawArrays(GL_POINTS, 0, numTriangles);
glBindVertexArray(0);
glUseProgram(0);
}
void MarchingCubesDoublePass::OnRender(Matrix mat) {
RenderPass1();
RenderPass2(mat);
}
_
実際のエラーは、_RenderPass1
_のglDrawArrays
の呼び出しです。 glBeginTransformFeedback
とglEndTransformFeedback
の呼び出しをコメントアウトすると、glDrawArrays
がエラーの生成を停止することに注意してください。間違っているものは何でも、それはおそらく何らかの形でフィードバックを変換することに関連しています。
編集8/18/12、午後9時:
GDEBuggerでNVIDIA GLExpert機能を見つけましたが、これは以前はよく知らなかったものです。これをオンにしたとき、_GL_INVALID_OPERATION
_、特に_The current operation is illegal in the current state: Buffer is mapped.
_に関して、より実質的な情報が得られました。そのため、上記のポイント1に到達しています。方法はわかりませんが。
コードのどこにでもglMapBuffer
または関連する関数を呼び出していません。 glMapBuffer
、glMapBufferARB
、glMapBufferRange
、glUnmapBuffer
、およびglUnmapBufferARB
の呼び出しでブレークするようにgDEBuggerを設定しましたが、どこでもブレークしませんでした。次に、RenderPass1
_の先頭にコードを追加して、わざわざバッファを明示的にマップ解除しました。エラーが解消されなかっただけでなく、glUnmapBuffer
への呼び出しによって_The current operation is illegal in the current state: Buffer is unbound or is already unmapped.
_が生成されるようになりました。使用しているバッファのどちらもマッピングされていない場合、エラーはどこから発生しますか?
編集8/12/12、午前12時:
GDEBuggerのGLExpertから取得しているエラーメッセージに基づいて、glBeginTransformFeedback
を呼び出すと、_GL_TRANSFORM_FEEDBACK_BUFFER
_にバインドされたバッファーがマップされているようです。具体的には、「テクスチャ、バッファ、および画像ビューア」でバッファをクリックすると、メッセージ_The current operation is illegal in the current state: Buffer must be bound and not mapped.
_が出力されます。ただし、glBeginTransformFeedback
とglEndTransformFeedback
の間にこれを追加すると:
_int bufferBinding;
glGetBufferParameteriv(GL_TRANSFORM_FEEDBACK_BUFFER, GL_BUFFER_MAPPED, &bufferBinding);
printf("Transform feedback buffer binding: %d\n", bufferBinding);
_
0を出力します。これは、_GL_TRANSFORM_FEEDBACK_BUFFER
_がマップされていないことを示します。このバッファーが別のバインディングポイントにマップされている場合、これはまだ0を返しますか?なぜglBeginTransformFeedback
がバッファーをマップして、変換フィードバックに使用できなくなるのでしょうか?
ここで学べば学ぶほど、混乱してしまいます。
10/10/12を編集:
ニコルボーラスのソリューションへの私の返信で示されているように、私は問題を見つけました。それは彼が見つけたものと同じです。
おそらく質問を投稿してから2週間後に発見しました。私はしばらくフラストレーションをあきらめ、最終的に戻ってきて、基本的にすべてをゼロから再実装し、古くて機能していないものを定期的に比較しました。完了したとき、新しいバージョンは機能し、間違ったバッファーをバインドしていたことがわかった違いを検索しました。
私はあなたの問題を理解しました:あなたは頂点データをソースしているのと同じバッファにレンダリングしています。
glBindVertexArray(vaoPass2);
vaoPass1
仕様から:
バッファーをバインドしたり、変換フィードバックとGLの他の目的の両方に使用しないでください。具体的には、バッファオブジェクトが変換フィードバックバッファバインディングポイントとGLの別の場所に同時にバインドされている場合、バッファへの書き込みまたはバッファからの読み取りは未定義の値を生成します。このようなバインディングの例には、ピクセルバッファーオブジェクトバインディングポイントへのReadPixelsや、MapBufferでマップされたバッファーへのクライアントアクセスが含まれます。
ここで、未定義の値を取得する必要があります。 GLエラーが該当するかどうかはわかりませんが、おそらくエラーであるはずです。
glDrawArrays
およびglDrawElements
が_GL_INVALID_OPERATION
_で失敗する別の(明らかに文書化されていない)ケース:
GL_INVALID_OPERATION
_が生成されます。 (glUniform1i(location, GL_TEXTURE0);
を意味するときに、誤ってglUniform1i(location, 0);
を実行していました。)glDraw*()
呼び出しが_GL_INVALID_OPERATION
_で失敗する可能性がある別の(文書化されていない)ケース:
GL_INVALID_OPERATION
_は、サンプラーのユニフォームが正しくないタイプのテクスチャにバインドされたテクスチャユニットに設定されている場合に生成されます。たとえば、_uniform sampler2D
_がglUniform1i(location, 0);
に設定されているが、_GL_TEXTURE0
_に_GL_TEXTURE_2D_ARRAY
_テクスチャバウンドがある場合。