我有一個非常大的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中做到這一點。
如果綁定程序改變的結果,那麼你應該調查這個第一。嘗試使用不依賴紋理的簡單着色器(輸出到處都是紅色)並查看是否有效。 Qt改變許多GL狀態,你不能依賴任何處於某種狀態的東西。 嘗試重置和重新綁定任何東西,包括像GL_DEPTH_TEST,視口等之類的東西。 –
我也在想。我正在閱讀有人提到Qt使用自己的着色器QGraphicsItems的相關帖子,所以我認爲我看到的白色盒子實際上並不是我的着色器的結果,而是已經綁定的另一個盒子當我綁定我的時候,我什麼都看不到,因爲我沒有做正確的事情(很可能是轉型)。 – Moohasha