2015-07-10 180 views
2

我有一个非常大的QGraphicsScene,可以包含非常大量的图形。我使用QGLWidget作为视口,以便我可以利用OpenGL来尝试改进某些东西的渲染效果。我创建了一个自定义的QGraphicsItem,我可以在一次渲染调用中使用相同的纹理绘制几个四边形,而不是在场景中有数百或数千个不同的QGraphicsItem,这些QGraphicsItem实际上都是以相同的方式绘制的,只是在不同的位置。在我自定义的QGraphicsItem的paint()方法中,我调用了beginNativePainting()和endNativePainting(),并在它们之间完成了我所有的OpenGL调用。QGraphicsScene&OpenGL片段着色器不工作

我想使用着色器程序,这样我可以在一定程度内的顶点着色器操作的顶点,所以我复制了Qt的OpenGL纹理例子使用着色器程序绘制纹理6个四边形。该示例工作得很好,但是当我尝试在QGraphicsItem的paint()方法中使用相同的方法时,我所有的四边形都被绘制成白色。我最好的猜测是我的片段着色器没有被使用。我甚至尝试在片段着色器中对颜色进行硬编码,而且没有任何变化。

这里是我的自定义QGraphicsItem类的源代码。

class BatchGraphics : public QGraphicsPixmapItem 
{ 
    enum {PROGRAM_VERTEX_ATTRIBUTE = 0, 
     PROGRAM_TEXCOORD_ATTRIBUTE = 1}; 

public: 
    BatchGraphics() 
    : _program(0), 
     _texture(0), 
     _dirty(false) 
    { 
    } 

    // Returns the custom bounding rect for this item which encompasses all quads 
    QRectF boundingRect() const 
    { 
    return _boundingRect; 
    } 

    // Add a quad to the batch. Only the center point is necessary 
    void addQuad(int id, float x, float y) 
    { 
    _quads.insert(id, QPointF(x, y)); 
    updateBoundingRect(); 
    _dirty = true; 
    } 

    // Remove a quad from the batch. 
    void removeQuad(int id) 
    { 
    if (_quads.contains(id)) 
    { 
     _quads.remove(id); 
     updateBoundingRect(); 
     _dirty = true; 
    } 
    } 

    // Return the number of quads in the batch 
    int count() {return _quads.count();} 

    // Paint the batch using a custom implementation. 
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) 
    { 
    // If the item is dirty (has been modified, update the geometry) 
    if (_dirty) { 
     updateGeometry(); 
    } 

    if (_program) 
    { 
     painter->beginNativePainting(); 

     // Enable GL states 
     //glEnable(GL_TEXTURE_2D); 

     // Set the MVP matrix 
     _program->setUniformValue("matrix", painter->transform()); 

     // Enable and set the vertex and texture attributes 
     _program->enableAttributeArray(PROGRAM_VERTEX_ATTRIBUTE); 
     _program->enableAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE); 
     _program->setAttributeArray(PROGRAM_VERTEX_ATTRIBUTE, GL_FLOAT, _vertices.constData(), 3, 5*sizeof(GLfloat)); 
     _program->setAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE, GL_FLOAT, _vertices.constData()+2, 2, 5*sizeof(GLfloat)); 

     // Bind the texture 
     _texture->bind(); 

     // Draw the arrays 
     glDrawArrays(GL_TRIANGLES, 0, _quads.count()*6); // 6 vertices per quad 

     painter->endNativePainting(); 
    } 
    } 

private: 
    // Initialize the shader and texture 
    void initialize() 
    { 
    // Create the OpenGL texture 
    _texture = new QOpenGLTexture(pixmap().toImage()); 

    // Vertex Shader 
    QOpenGLShader *vshader = new QOpenGLShader(QOpenGLShader::Vertex); 
    const char *vsrc = 
     "attribute highp vec4 vertex;\n" 
     "attribute mediump vec4 texCoord;\n" 
     "varying mediump vec4 texc;\n" 
     "uniform mediump mat4 matrix;\n" 
     "void main(void)\n" 
     "{\n" 
     " gl_Position = matrix * vertex;\n" 
     " texc = texCoord;\n" 
     "}\n"; 
    vshader->compileSourceCode(vsrc); 

    // Fragment Shader 
    QOpenGLShader *fshader = new QOpenGLShader(QOpenGLShader::Fragment); 
    const char *fsrc = 
     "uniform   sampler2D texture;\n" 
     "varying mediump vec4  texc;\n" 
     "void main(void)\n" 
     "{\n" 
     " gl_FragColor = texture2D(texture, texc.st);\n" 
     "}\n"; 
    fshader->compileSourceCode(fsrc); 

    // Program 
    _program = new QOpenGLShaderProgram; 
    _program->addShader(vshader); 
    _program->addShader(fshader); 
    _program->bindAttributeLocation("vertex", PROGRAM_VERTEX_ATTRIBUTE); 
    _program->bindAttributeLocation("texCoord", PROGRAM_TEXCOORD_ATTRIBUTE); 
    _program->link(); 

    _program->bind(); 
    _program->setUniformValue("texture", 0); 
    } 

    // Update the vertex array. Calls initialize the first time. 
    void updateGeometry() 
    { 
    if (_program == 0) { 
     initialize(); 
    } 

    _vertices.clear(); 

    // Half pixmap size 
    QPointF s = QPointF(pixmap().width()/2, pixmap().height()/2); 

    // Build vertex data for each quad 
    foreach (const QPointF& point, _quads) 
    { 
     // Top Left 
     _vertices << point.x()-s.x(); // x 
     _vertices << point.y()-s.y(); // y 
     _vertices << 1;    // z 
     _vertices << 0;    // tu 
     _vertices << 1;    // tv 

     // Top Right 
     _vertices << point.x()+s.x(); // x 
     _vertices << point.y()-s.y(); // y 
     _vertices << 1;    // z 
     _vertices << 1;    // tu 
     _vertices << 1;    // tv 

     // Bottom Left 
     _vertices << point.x()-s.x(); // x 
     _vertices << point.y()+s.y(); // y 
     _vertices << 1;    // z 
     _vertices << 0;    // tu 
     _vertices << 0;    // tv 

     // Top Right 
     _vertices << point.x()+s.x(); // x 
     _vertices << point.y()-s.y(); // y 
     _vertices << 1;    // z 
     _vertices << 1;    // tu 
     _vertices << 1;    // tv 

     // Bottom Left 
     _vertices << point.x()-s.x(); // x 
     _vertices << point.y()+s.y(); // y 
     _vertices << 1;    // z 
     _vertices << 0;    // tu 
     _vertices << 0;    // tv 

     // Bottom Right 
     _vertices << point.x()+s.x(); // x 
     _vertices << point.y()+s.y(); // y 
     _vertices << 1;    // z 
     _vertices << 1;    // tu 
     _vertices << 0;    // tv 
    } 

    _dirty = false; 
    } 

private: 
    // Updates the bounding rect based on the quads in the batch. 
    void updateBoundingRect() 
    { 
    prepareGeometryChange(); 

    double left = 9999; 
    double right = -9999; 
    double top = 9999; 
    double bottom = -9999; 

    double w = pixmap().width()/2; 
    double h = pixmap().width()/2; 

    foreach (const QPointF& p, _quads) 
    { 
     left = qMin(left, p.x()-w); 
     right = qMax(right, p.x()+w); 
     top = qMin(top, p.y()-h); 
     bottom = qMax(bottom, p.y()+h); 
    } 

    _boundingRect = QRectF(left, top, (right-left), (bottom-top)); 
    } 

private: 
    QOpenGLShaderProgram* _program; 
    QOpenGLTexture* _texture; 
    QRectF _boundingRect; 
    QMap<int, QPointF> _quads; 
    QVector<GLfloat> _vertices; 
    bool _dirty; 
}; 

我明白渲染管线以及如何使用着色器的基础知识,但就事情和其他的OpenGL方法之间的依赖关系必须使用某些功能时,可以称为我的是很笨。我可以使用固定的函数管道方法使纹理渲染,但这是旧派,就像我说的,我希望能够在顶点着色器中操作顶点时,一旦我得到这个工作。

创建QGLWidget时,我没有做任何特别的事情,它的QGLFormat最终为2.0。我也尝试调用glEnable(GL_TEXTURE_2D),但这只是使四边形呈现黑色而不是白色。每次调用paint()时,我也尝试过绑定程序,因为可能Qt在幕后的其他地方绑定了不同的着色器,但这只会导致NOTHING出现。

任何人都可以提供任何帮助吗?我无法弄清楚为什么这种方法在Qt纹理的例子中工作正常,但不是当我尝试在QGraphicsItem中做到这一点。

+0

如果绑定程序改变的结果,那么你应该调查这个第一。尝试使用不依赖纹理的简单着色器(输出到处都是红色)并查看是否有效。 Qt改变许多GL状态,你不能依赖任何处于某种状态的东西。 尝试重置和重新绑定任何东西,包括像GL_DEPTH_TEST,视口等之类的东西。 –

+0

我也在想。我正在阅读有人提到Qt使用自己的着色器QGraphicsItems的相关帖子,所以我认为我看到的白色盒子实际上并不是我的着色器的结果,而是已经绑定的另一个盒子当我绑定我的时候,我什么都看不到,因为我没有做正确的事情(很可能是转型)。 – Moohasha

回答

1

我看了Qt的源代码后发现了什么,当beginNativePainting()时会发生什么。首先,每次调用paint()时,我都必须绑定着色器,其次我必须获得正确的MVP矩阵。

我试图将QPainter的变换传递给我的着色器来充当模型视图投影矩阵,但变换只是模型视图矩阵。我还需要获得投影矩阵,当调用beginNativePainting()时,Qt会设置投影矩阵。

我从OpenGL直接获取了项目和模型视图矩阵,并在绑定我的纹理和presto后将它们组合到我的着色器中!有效!

下面是相关的修改我不得不作出:

painter->beginNativePainting(); 

// Enable GL states 
//glEnable(GL_TEXTURE_2D); 

// === Begin New Code ====== 
// Bind my program 
_program->bind(); 

QMatrix4x4 proj; 
glGetFloatv(GL_PROJECTION_MATRIX, proj.data()); 

QMatrix4x4 model; 
glGetFloatv(GL_MODELVIEW_MATRIX, model.data()); 

// Set the MVP matrix 
_program->setUniformValue("matrix", proj * model); 
// === End New Code ====== 

// Enable and set the vertex and texture attributes 
_program->enableAttributeArray(PROGRAM_VERTEX_ATTRIBUTE); 
_program->enableAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE); 
_program->setAttributeArray(PROGRAM_VERTEX_ATTRIBUTE, GL_FLOAT, _vertices.constData(), 3, 5*sizeof(GLfloat)); 
_program->setAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE, GL_FLOAT, _vertices.constData()+2, 2, 5*sizeof(GLfloat));