我有一个这种技术的工作实现,用于实例几何体的视锥体剔除。该技术的要点是我们使用顶点着色器来检查对象的边界是否位于视锥内,如果是,我们使用变换反馈缓冲区和几何着色器将该对象的位置输出到质地。然后,在实际渲染过程中,我们可以使用该纹理以及我们发出多少位置的查询,以获取我们正在渲染的对象的相关位置数据,以及在调用 glDrawElementsInstanced 时指定的绘制次数. 我所做的和文章所做的之间的一个区别是,我向纹理发出了一个完整的变换矩阵,而不是一个简单的位置向量,但我怀疑这对我的问题有任何影响。
实际问题:目前我有这个设置,因此,对于每个被渲染的对象类型(即树、盒子、岩石等),实际的渲染过程会立即跟随平截头体剔除渲染过程。这有效,并给出了预期的结果。然而,我想要做的是检查我所有的绘图命令并首先对各种对象进行所有平截头体剔除,然后才进行所有实际渲染,以避免一堆不必要的状态更改(即切换回和在着色器程序之间第四次)。然而,当我这样做时,我遇到了以前建立的纹理——我用于在实际渲染过程中读取位置的纹理——似乎都被最新的截锥体剔除函数调用覆盖了,
例如:我按顺序渲染了 4 棵树、10 个盒子和 3 块岩石,而我将看到的是一棵树、一个盒子和一块岩石,在所有(三个)位置上,我预计只有 3 块岩石成为。我一生都无法弄清楚为什么会这样,因为每次调用该函数时,我都非常清楚地将新缓冲区和纹理绑定到 TRANSFORM_FEEDBACK_BUFFER。为什么以前使用的纹理仍然从最新调用中接收到新数据?
截头体剔除功能的 C 代码:
void fcullidraw(drawcommand *tar) {
/* printf("Fculling %s\n", tar->res->name); */
mesh *rmesh = &tar->res->amod->meshes[0];
/* glDeleteTextures(1, &rmesh->ctex); */
if(rmesh->ctbuf == 0)
glGenBuffers(1, &rmesh->ctbuf);
glBindBuffer(GL_TEXTURE_BUFFER, rmesh->ctbuf);
glBufferData(GL_TEXTURE_BUFFER, sizeof(instancedata) * tar->nodraws, NULL, GL_DYNAMIC_COPY);
if(rmesh->ctex == 0)
glGenTextures(1, &rmesh->ctex);
glBindTexture(GL_TEXTURE_BUFFER, rmesh->ctex);
glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, rmesh->ctbuf);
if(rmesh->cquery == 0)
glGenQueries(1, &rmesh->cquery);
checkactiveshader(tar->tar, findshader("icull"));
glEnable(GL_RASTERIZER_DISCARD);
glUniform1f(activeshader->radius, tar->res->amesh->bbox.radius);
glUniform3fv(activeshader->extent, 1, (const GLfloat*)&tar->res->amesh->bbox.ext);
glUniform3fv(activeshader->cp, 1, (const GLfloat*)&tar->res->amesh->bbox.cp);
glBindVertexArray(tar->res->amod->meshes[0].vao);
glBindBuffer(GL_ARRAY_BUFFER, tar->res->amod->meshes[0].posarray);
glBufferData(GL_ARRAY_BUFFER, sizeof(mat4_t) * tar->nodraws, tar->posarray, GL_DYNAMIC_DRAW);
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, rmesh->ctbuf);
glBeginTransformFeedback(GL_POINTS);
glBeginQuery(GL_PRIMITIVES_GENERATED, rmesh->cquery);
glDrawArrays(GL_POINTS, 0, tar->nodraws);
glEndQuery(GL_PRIMITIVES_GENERATED);
glEndTransformFeedback();
glDisable(GL_RASTERIZER_DISCARD);
glGetQueryObjectuiv(rmesh->cquery, GL_QUERY_RESULT, &rmesh->visibleinstances);
}
tar 和 rmesh 显然在每次调用此函数时有所不同。请注意,我在这里留下了几行注释,其中包含删除每个渲染周期之间的缓冲区和纹理的代码,而不是简单地覆盖它们,而是使用该代码对错误模式没有影响。
我难住了。我觉得纹理和缓冲区定义明确并且清楚地保持分开,所以我不明白以前调用 fcullidraw 的纹理如何仍以某种方式绑定并被 TransformFeedback 覆盖,如果这确实发生了,而且它看起来肯定是这样,因为早期的物体会非常整齐地读取岩石的整个变换矩阵,包括“正确”的旋转、平移和一切。
链接的文章确实按照我想要的顺序执行操作——即首先重复截锥体剔除,然后重复渲染——我不确定我看到我做了什么不同。可能是一些小而明显的事情,我可能是个白痴,但在那种情况下,我很想知道我为什么以及如何成为那个人。
编辑:我推动并更新了我的实现,对原始技术进行了改进,在这里建议,它完全摆脱了写入纹理的方法,而是简单地写入绑定到 VAO 的缓冲区,并设置为每个渲染实例使用 VertexAttribDivisor 更新一次。这种方法总体上看起来更干净,顺便说一下,根本没有我原来的问题的额外副作用,因为我不再写入和上传纹理。因此,这对我来说不再是一个实际问题,但我仍然无法回答理论问题,所以如果有人有想法,我仍然全神贯注。