2015-07-10 173 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));