我一直在尝试使用 OpenGL 和 GLSL 编写 Marching Cubes 算法的两遍 GPU 实现,类似于 GPU Gems 3 第一章中详述的那个。但是,glDrawArrays
在我的第一遍调用中始终以GL_INVALID_OPERATION
.
我查找了所有我能找到的文档,并找到了glDrawArrays
可能引发该错误的这些条件:
GL_INVALID_OPERATION
如果非零缓冲区对象名称绑定到启用的数组或GL_DRAW_INDIRECT_BUFFER
绑定,并且缓冲区对象的数据存储当前已映射,则生成。GL_INVALID_OPERATION
如果glDrawArrays
在 的执行glBegin
和对应的 之间执行,则生成glEnd
。GL_INVALID_OPERATION
将由glDrawArrays
或glDrawElements
如果当前程序对象中的任何两个活动采样器属于不同类型,但引用相同的纹理图像单元。GL_INVALID_OPERATION
如果几何着色器处于活动状态并且模式与当前安装的程序对象中几何着色器的输入基元类型不兼容,则会生成。GL_INVALID_OPERATION
如果模式是GL_PATCHES
并且没有曲面细分控制着色器处于活动状态,则生成。GL_INVALID_OPERATION
如果将图元的顶点记录到用于变换反馈目的的缓冲区对象会导致超出任何缓冲区对象大小的限制,或者超出由 设置的结束位置偏移量 + 大小 - 1,则会生成glBindBufferRange
。GL_INVALID_OPERATION
如果不存在几何着色器,则生成glDrawArrays()
变换反馈,并且模式不是允许的模式之一。GL_INVALID_OPERATION
如果存在几何着色器,则生成glDrawArrays()
变换反馈,并且几何着色器的输出图元类型与变换反馈图元模式不匹配。GL_INVALID_OPERATION
如果绑定的着色器程序无效,则生成。- EDIT 10/10/12:
GL_INVALID_OPERATION
在使用变换反馈时生成,并且绑定到变换反馈绑定点的缓冲区也绑定到数组缓冲区绑定点。这是我遇到的问题,因为我绑定的缓冲区有错字。虽然规范确实声明这是非法的,但在我找到的任何文档中,它都没有列在 glDrawArrays 下作为它可能引发错误的原因之一。
不幸的是,我找不到任何一份官方文档涵盖其中的 3 个以上。我不得不从众多来源收集这份清单。第 7 点和第 8 点实际上来自文档glBeginTransformFeedback
,而第 9 点似乎根本没有记录。我在某处的论坛帖子中发现了它。但是,我仍然不认为这个列表是完整的,因为这些似乎都不能解释我得到的错误。
- 我根本没有在我的程序中的任何地方映射任何缓冲区。
- 我正在使用核心配置文件,因此
glBegin
甚至glEnd
不可用。 - 我有两个采样器,它们的类型不同,但它们肯定映射到不同的纹理。
- 几何着色器处于活动状态,但它的输入布局是
layout (points) in
,并且glDrawArrays
正在使用 调用GL_POINTS
。 - 我没有使用
GL_PATCHES
或任何类型的镶嵌着色器。 - 我已经确保我分配了几何着色器可能输出的最大空间量。然后我试着把它翻了两番。没有帮助。
- 有一个几何着色器。看下一点。
- 正在使用变换反馈,并且有一个几何着色器,但输出布局是
layout (points) out
并且glBeginTransformFeedback
被调用GL_POINTS
。 - 我尝试在调用 to
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);
}
glDrawArrays
实际错误是对in的调用RenderPass1
。值得注意的是,如果我注释掉对glBeginTransformFeedback
and的调用glEndTransformFeedback
,则glDrawArrays
停止生成错误。所以不管有什么问题,它可能与变换反馈有关。
2012 年 8 月 18 日晚上 9 点编辑:
我刚刚在 gDEBugger 中发现了 NVIDIA GLExpert 功能,这是我以前不熟悉的。当我打开它时,它提供了更多关于 的信息GL_INVALID_OPERATION
,特别是The current operation is illegal in the current state: Buffer is mapped.
. 所以我遇到了上面的第 1 点。虽然我不知道怎么做。
我的代码中的任何地方都没有调用glMapBuffer
或任何相关函数。glMapBuffer
我将 gDEBugger 设置为在对、glMapBufferARB
、和的任何调用时中断glMapBufferRange
,并且它在任何地方都没有中断。然后我在开头添加了代码来显式地取消映射打扰缓冲区。错误不仅没有消失,而且对now 的调用都 generate 。因此,如果我使用的两个缓冲区都没有映射,那么错误来自哪里?glUnmapBuffer
glUnmapBufferARB
RenderPass1
glUnmapBuffer
The current operation is illegal in the current state: Buffer is unbound or is already unmapped.
2012 年 8 月 19 日上午 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
and之间添加这个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
映射缓冲区,从而使其无法用于转换反馈?
我在这里学到的越多,我就越困惑。
2012 年 10 月 10 日编辑:
正如我在下面对 Nicol Bolas 解决方案的回复中指出的那样,我发现了问题,并且与他发现的问题相同:由于一个愚蠢的错字,我将同一个缓冲区绑定到输入和输出绑定点。
我在发布问题两周后发现了它。我曾经沮丧地放弃了一段时间,最终回来并基本上从头开始重新实现整个事情,定期比较旧的、不工作的部分和部分。当我完成后,新版本就起作用了,当我搜索出差异时,我发现我绑定了错误的缓冲区。