2013-02-02 73 views
0

我正在尝试使用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代码,原因超出了这个问题的范围。

+0

你知道,这里有occlusion查询,它完全是你想要手动完成的。渲染一个原语后,它会告诉你,产生的碎片有多少被遮挡/绘制到帧缓冲区。我认为你最好使用它而不是手动计算像素。 – datenwolf

+0

有趣!你是指http://www.opengl.org/registry/specs/ARB/occlusion_query.txt?这可能会在一个捏。该计划是在着色器中用正常矢量做一些数学运算,所以它仍然是能够从片段着色器读取输出的理想选择。但是,如果我只是有遮挡查询,我想我可以在CPU上做矢量数学运算,尽管它会变慢。 – AngryPuffin

+0

是的,我指的是,但它已成为更高版本OpenGL的核心功能,请参阅http://www.opengl.org/sdk/docs/man/xhtml/glGenQueries.xml及以下链接。为了使遮挡查询工作,您仍然可以绘制到帧缓冲区,因此您可以在着色器中进行数学运算,因为遮挡查询仅收集统计信息并且不会更改渲染结果。不过,它允许您在CPU上保存一些重要的周期。 – datenwolf

回答

1

这里有很多Qt的东西,几乎不可能找到实际的OpenGL调用。但你似乎有两个问题:

  1. 你正在渲染到屏幕上。您的屏幕使用某种标准化的整数图像格式。这基本上意味着“浮动,但占用8位”。你从着色器写入整数。这些不匹配。因此,您的渲染产生未定义的行为。

    你需要做的是呈现给包含GL_R8UI纹理的FBO。然后您的uint片段着色器输出类型将与您的缓冲区匹配。你可能也需要一个深度缓冲区。

  2. 你从来没有真的读取的像素数据。 QGLBuffer::read缓冲区对象中读取。但是你还没有将任何东西放入缓冲区对象中。您从未告诉OpenGL复制您渲染的帧缓冲区数据并将其存储在缓冲区对象中。你需要先这样做;做完之后,你可以从中读取。

    在渲染到您的FBO后,您需要call glReadPixels。当你这样做的时候,你需要提供正确的pixel transfer parameters来表达你已经呈现的内容。即,对于type,您需要使用GL_RED_INTEGER作为formatGL_UNSIGNED_BYTE。而且,由于您正在阅读pixel buffer,因此在阅读之前,您需要确保它受到约束。

+0

感谢您的帮助,但这使我感到困惑。 1)FragColor输出是颜色输出(3D浮点数),并在屏幕上正确渲染。 2)为什么我不想从缓冲区对象读取数据?缓冲区对象位于GPU中。我已经试图将着色器中的输出变量“FragId”连接到此缓冲区。这是否不符合我的想法?它对于输入变量很好。 – AngryPuffin

+0

我想我没有时间编辑我的评论。第1部分的补充说明:......它在屏幕上正确渲染。据我了解,GLSL自动渲染第一个浮动vec4;它不会尝试渲染稍后的输出。我无法从片段着色器访问多个输出变量? – AngryPuffin