我正在尝试使用OpenGL进行复杂几何图形的可见性测试。我想要做的事情很简单:为每个原语分配一个整数ID,然后计算具有该ID的像素数量。这使我可以计算每个基元的相对可见区域。 (最终,这将扩展到可见区域上的一些较小的有限元计算。)将片段着色器输出读入应用程序内存
我的问题是这样的。我试图将片段着色器的输出读入我的应用程序内存中:特别是原始ID输出。我正在使用QT 4.7.4及其OpenGL包装类。当我绑定并启用一个缓冲区(一个“PixelPack”缓冲区),并尝试从OpenGL缓冲区读入内存时,它会报告读取成功。但是存储在数组中的值并不是我所期望的 - 它们都是0,即使出于测试目的,我已经为所有基元设置了ID。
这是我的片段着色器:
#version 130
in vec4 Color;
flat in uint VertId;
out vec4 FragColor;
out uint FragId;
void main()
{
FragColor = Color;
// Changed to simpler version for debugging.
// FragId = VertId;
FragId = uint(1);
}
这是我的应用程序代码,以汽提掉一些不相关部分(测试工具进行连接等):
#include <QtOpenGL/QGLShader>
#include <QtOpenGL/QGLShaderProgram>
#include <QtOpenGL/QGLBuffer>
using namespace std;
string loadSource(string file);
int
testSelfShadow::
shader(ostream& error)
{
bool fail = false;
// Create the OpenGL context.
int argc = 0;
char* argv;
QApplication* app = new QApplication(argc, &argv);
QGLWidget* widget = new QGLWidget();
widget->makeCurrent();
// Create the shader program.
QGLShaderProgram* prog = new QGLShaderProgram();
bool success = false;
success = prog->addShaderFromSourceCode(QGLShader::Vertex,
loadSource("vertex.glsl").c_str());
if (! success)
{
ErrorOStream msg;
msg << "Error trying to load vertex shader into a shader program.\n"
<< prog->log().toStdString();
throw ERRORLOG(msg.str());
}
success = prog->addShaderFromSourceCode(QGLShader::Fragment,
loadSource("fragment.glsl").c_str());
if (! success)
{
ErrorOStream msg;
msg << "Error trying to load fragment shader into a shader program.\n"
<< prog->log().toStdString();
throw ERRORLOG(msg.str());
}
success = prog->link();
if (! success)
{
ErrorOStream msg;
msg << "Error trying to link shaders into a shader program.\n"
<< prog->log().toStdString();
throw ERRORLOG(msg.str());
}
prog->bind();
// Create the buffer for vertex position.
QGLBuffer* vBuf = new QGLBuffer(QGLBuffer::VertexBuffer);
vBuf->create();
vBuf->setUsagePattern(QGLBuffer::DynamicDraw);
vBuf->bind();
GLfloat vertices[] = {
-1.0f, -1.0f, 0.0f, 1.0f,
-1.0f, 0.0f, 0.0f, 1.0f,
1.0f, 0.0f, 0.0f, 1.0f,
1.0f, -1.0f, 0.0f, 1.0f,
-1.0f, 0.0f, 0.1f, 1.0f,
-1.0f, 1.0f, 0.1f, 1.0f,
1.0f, 1.0f, 0.1f, 1.0f,
1.0f, 0.0f, 0.1f, 1.0f };
vBuf->allocate(vertices, sizeof(vertices));
prog->setAttributeBuffer("Vertex", GL_FLOAT, 0, 4, 0);
prog->enableAttributeArray("Vertex");
// Create the buffer for Grayscale brightness value.
// Not important for final program, just for debugging during
// development.
QGLBuffer* bBuf = new QGLBuffer(QGLBuffer::VertexBuffer);
bBuf->create();
bBuf->bind();
GLfloat brightness[] = {
1.0, 0.9, 0.8, 0.7,
0.5, 0.4, 0.3, 0.2
};
bBuf->allocate(brightness, sizeof(brightness));
prog->setAttributeBuffer("Brightness", GL_FLOAT, 0, 1, 0);
prog->enableAttributeArray("Brightness");
// Create the buffer for polygon ID.
QGLBuffer* idBuf = new QGLBuffer(QGLBuffer::VertexBuffer);
idBuf->create();
idBuf->bind();
GLuint polyId[] = {
1, 1, 1, 1,
2, 2, 2, 2
};
idBuf->allocate(polyId, sizeof(polyId));
prog->setAttributeBuffer("PolyId", GL_UNSIGNED_INT, 0, 1, 0);
prog->enableAttributeArray("PolyId");
// Create the index buffer. Not trying to do any optimization
// here yet.
QGLBuffer* iBuf = new QGLBuffer(QGLBuffer::IndexBuffer);
iBuf->create();
iBuf->bind();
GLuint indices[] = {
0, 1, 2, 3, 4, 5, 6, 7
};
iBuf->setUsagePattern(QGLBuffer::StaticDraw);
iBuf->allocate(indices, sizeof(indices));
// Create the buffer for reading back polygon id per fragment.
QGLBuffer* fBuf = new QGLBuffer(QGLBuffer::PixelPackBuffer);
fBuf->create();
fBuf->setUsagePattern(QGLBuffer::DynamicRead);
fBuf->bind();
fBuf->allocate(640 * 480 * sizeof(GLuint));
prog->setAttributeBuffer("FragId", GL_UNSIGNED_INT, 0, 1, 0);
prog->enableAttributeArray("FragId");
GLuint* fBufData = new GLuint[ 640 * 480 * sizeof(GLuint) ];
glDrawElements(GL_QUADS, 8, GL_UNSIGNED_INT, 0);
widget->show();
widget->updateGL();
// Trying this two different ways; neither way works.
bool readSuccess = fBuf->read(0, fBufData, 640 * 480 * sizeof(GLuint));
GLuint* fBufMap =
static_cast< GLuint* >(fBuf->map(QGLBuffer::ReadOnly));
cout << "\n"
<< "Read Successful: " << readSuccess << endl;
cout << "Buffer map location and sample data: "
<< fBufMap << ":" << fBufMap[640] << endl;
cout << "Read data pointer: " << fBufData << endl;
cout << "Sample fragment ID: " << fBufData[ 640 * 480/2 ] << endl;
app->exec();
return fail;
}
下面是样本输出程序运行:
Read Successful: true
Buffer map location and sample data: 0x5a5d9000:0
Read data pointer: 0x59e48008
Sample fragment ID: 0
这不是我所期望的。我期望所有的片段ID都是1,因为我明确地在片段着色器中设置了FragId = uint(1)。我是否设置了我的阅读错误?我是否在缓冲区绑定或启用名称时做错了什么?
如果可能的话,我宁愿使用QT代码,原因超出了这个问题的范围。
你知道,这里有occlusion查询,它完全是你想要手动完成的。渲染一个原语后,它会告诉你,产生的碎片有多少被遮挡/绘制到帧缓冲区。我认为你最好使用它而不是手动计算像素。 – datenwolf
有趣!你是指http://www.opengl.org/registry/specs/ARB/occlusion_query.txt?这可能会在一个捏。该计划是在着色器中用正常矢量做一些数学运算,所以它仍然是能够从片段着色器读取输出的理想选择。但是,如果我只是有遮挡查询,我想我可以在CPU上做矢量数学运算,尽管它会变慢。 – AngryPuffin
是的,我指的是,但它已成为更高版本OpenGL的核心功能,请参阅http://www.opengl.org/sdk/docs/man/xhtml/glGenQueries.xml及以下链接。为了使遮挡查询工作,您仍然可以绘制到帧缓冲区,因此您可以在着色器中进行数学运算,因为遮挡查询仅收集统计信息并且不会更改渲染结果。不过,它允许您在CPU上保存一些重要的周期。 – datenwolf