如何在Qt 5中使用VAOs实例化



我正试图围绕如何适当地使用VAOs进行实例渲染(特别是在Qt 5.2中,使用OpenGL 3.3)。我的理解是,VAO保存了vbo和相关属性的状态,这样您就不需要担心在绘制时绑定和启用所有内容,您只需绑定VAO。但是对于实例化,您通常有多个vbo。如何避免绑定它们呢?或者我只需要为我的每个顶点数据和每个实例数据使用单个VBO吗?

我一直在看一些教程,例如:http://ogldev.atspace.co.uk/www/tutorial33/tutorial33.html

在我看来,他所做的是为他的每个顶点数据使用VAO,而不是为他的每个实例数据。我尝试过用基于qt的代码做同样的事情,但它不适合我(可能是因为我不完全理解它是如何工作的……)当绘图发生时,他的实例数据不应该仍然需要绑定吗?)

一些虚拟代码…这有点傻,我只是画了两个三角形的单个实例,用透视矩阵作为每个实例的属性。

glwindow.cpp:

#include "glwindow.h"
#include <QColor>
#include <QMatrix4x4>
#include <QVector>
#include <QVector3D>
#include <QVector4D>
#include <QDebug>

GLWindow::GLWindow(QWindow *parent) 
  : QWindow(parent)
  , _vbo(QOpenGLBuffer::VertexBuffer)
  , _matbo(QOpenGLBuffer::VertexBuffer)
  , _context(0)
{
  setSurfaceType(QWindow::OpenGLSurface);
}
GLWindow::~GLWindow()
{}
void GLWindow::initGL()
{  
  setupShaders();
  _program->bind();
  _positionAttr = _program->attributeLocation("position");
  _colourAttr = _program->attributeLocation("colour");
  _matrixAttr = _program->attributeLocation("matrix");
  QVector<QVector3D> triangles;
  triangles << QVector3D(-0.5, 0.5, 1) << QVector3D(-0.5, -0.5, 1) << QVector3D(0.5, -0.5, 1);
  triangles << QVector3D(0.5, 0.5, 0.5) << QVector3D(-0.5, -0.5, 0.5) << QVector3D(0.5, -0.5, 0.5);
  QVector<QVector3D> colours;
  colours << QVector3D(1, 0, 0) << QVector3D(0, 1, 0) << QVector3D(0, 0, 1);
  colours << QVector3D(1, 1, 1) << QVector3D(1, 1, 1) << QVector3D(1, 1, 1);
  _vao.create();
  _vao.bind();
  _vbo.create();
  _vbo.setUsagePattern(QOpenGLBuffer::StaticDraw);
  _vbo.bind();
  size_t positionSize = triangles.size() * sizeof(QVector3D);
  size_t colourSize = colours.size() * sizeof(QVector3D);
  _vbo.allocate(positionSize + colourSize);
  _vbo.bind();
  _vbo.write(0, triangles.constData(), positionSize);
  _vbo.write(positionSize, colours.constData(), colourSize);
  _colourOffset = positionSize;
  _program->setAttributeBuffer(_positionAttr, GL_FLOAT, 0, 3, 0);
  _program->setAttributeBuffer(_colourAttr, GL_FLOAT, _colourOffset, 3, 0);
  _program->enableAttributeArray(_positionAttr);  
  _program->enableAttributeArray(_colourAttr);
  _vao.release();
  _matbo.create();
  _matbo.setUsagePattern(QOpenGLBuffer::StaticDraw);
  _matbo.bind();
  _matbo.allocate(4 * sizeof(QVector4D));
  _program->setAttributeBuffer(_matrixAttr, GL_FLOAT, 0, 4, 4 * sizeof(QVector4D));
  _program->enableAttributeArray(_matrixAttr);
  _func330->glVertexAttribDivisor(_matrixAttr, 1);
  _matbo.release();
  _program->release();
  resizeGL(width(), height());
}
void GLWindow::resizeGL(int w, int h)
{
  glViewport(0, 0, w, h);
}
void GLWindow::paintGL()
{
  if (! _context) // not yet initialized
    return;
  _context->makeCurrent(this);
  QColor background(Qt::black);
  glClearColor(background.redF(), background.greenF(), background.blueF(), 1.0f);
  glClearDepth(1.0f);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  QMatrix4x4 matrix;
  matrix.perspective(60, 4.0/3.0, 0.1, 100.0);
  matrix.translate(0, 0, -2);
  _program->bind();
  _matbo.bind();
  _matbo.write(0, matrix.constData(), 4 * sizeof(QVector4D));
  _vao.bind();
  glEnable(GL_DEPTH_TEST);
  _func330->glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 1);
  _vao.release();
  _program->release();
  _context->swapBuffers(this);
  _context->doneCurrent();
}
void GLWindow::setupShaders()
{
  QString vShaderSrc("#version 330n"
    "layout(location = 0) in vec4 position;n"
    "layout(location = 1) in vec4 colour;n"
    "layout(location = 2) in mat4 matrix;n"
    "smooth out vec4 col;n"
    "void main() {n"
    "   col = colour;n"
    "   gl_Position = matrix * position;n"
    "}n");
  QString fShaderSrc("#version 330n"
    "smooth in vec4 col;n"
    "void main() {n"
    "   gl_FragColor = col;n"
    "}n");

  _program = new QOpenGLShaderProgram(this);
  _program->addShaderFromSourceCode(QOpenGLShader::Vertex, vShaderSrc);
  _program->addShaderFromSourceCode(QOpenGLShader::Fragment, fShaderSrc);
  _program->link();

}
void GLWindow::exposeEvent(QExposeEvent *event)
{
  Q_UNUSED(event);
  if (isExposed())
  {
    if (! _context)
    {
      _context = new QOpenGLContext(this);
      QSurfaceFormat format(requestedFormat());
      format.setVersion(3,3);
      format.setDepthBufferSize(24);
      _context->setFormat(format);
      _context->create();
      _context->makeCurrent(this);
      initializeOpenGLFunctions();
      _func330 = _context->versionFunctions<QOpenGLFunctions_3_3_Core>();
      if (_func330)
        _func330->initializeOpenGLFunctions();
      else
      {
        qWarning() << "Could not obtain required OpenGL context version";
        exit(1);
      }
      initGL();
    }
    paintGL();
  }
}

glwindow.h:

#ifndef GL_WINDOW_H
#define GL_WINDOW_H
#include <QExposeEvent>
#include <QSurfaceFormat>
#include <QWindow>
#include <QOpenGLBuffer>
#include <QOpenGLContext>
#include <QOpenGLFunctions>
#include <QOpenGLFunctions_3_3_Core>
#include <QOpenGLShaderProgram>
#include <QOpenGLVertexArrayObject>

class GLWindow : public QWindow, protected QOpenGLFunctions
{
  Q_OBJECT
public:
  GLWindow(QWindow * = 0);
  virtual ~GLWindow();
  void initGL();
  void paintGL();
  void resizeGL(int, int);
protected:
  virtual void exposeEvent(QExposeEvent *);

private:
  void setupShaders();
  QOpenGLBuffer _vbo;
  QOpenGLBuffer _matbo;
  QOpenGLContext *_context;
  QOpenGLShaderProgram *_program;
  QOpenGLVertexArrayObject _vao;
  QOpenGLFunctions_3_3_Core *_func330;
  GLuint _positionAttr;
  GLuint _colourAttr;
  GLuint _matrixAttr;
  size_t _colourOffset;
} ; 
#endif

glbuffertest.cpp:

#include <QGuiApplication>
#include <QSurfaceFormat>
#include "glwindow.h"

int main(int argc, char **argv)
{
  QGuiApplication app(argc, argv);
  GLWindow window;  
  window.resize(400, 400);
  window.show();
  return app.exec();
}

glbuffertest.pro:

######################################################################
# Automatically generated by qmake (3.0) Fri May 16 09:49:41 2014
######################################################################
TEMPLATE = app
TARGET = glbuffertest
INCLUDEPATH += .
CONFIG += qt debug
# Input
SOURCES += glbuffertest.cpp glwindow.cpp
HEADERS += glwindow.h

更新:我试过摆脱我的_matbo缓冲区,而是把矩阵数据放入相同的VBO作为位置和颜色属性,但它不适合我。我的initGL函数现在看起来像:

void GLWindow::initGL()
{  
  setupShaders();
  _program->bind();
  _positionAttr = _program->attributeLocation("position");
  _colourAttr = _program->attributeLocation("colour");
  _matrixAttr = _program->attributeLocation("matrix");
  QVector<QVector3D> triangles;
  triangles << QVector3D(-0.5, 0.5, 1) << QVector3D(-0.5, -0.5, 1) << QVector3D(0.5, -0.5, 1);
  triangles << QVector3D(0.5, 0.5, 0.5) << QVector3D(-0.5, -0.5, 0.5) << QVector3D(0.5, -0.5, 0.5);
  QVector<QVector3D> colours;
  colours << QVector3D(1, 0, 0) << QVector3D(0, 1, 0) << QVector3D(0, 0, 1);
  colours << QVector3D(1, 1, 1) << QVector3D(1, 1, 1) << QVector3D(1, 1, 1);
  _vao.create();
  _vao.bind();
  _vbo.create();
  _vbo.setUsagePattern(QOpenGLBuffer::StaticDraw);
  _vbo.bind();
  size_t positionSize = triangles.size() * sizeof(QVector3D);
  size_t colourSize = colours.size() * sizeof(QVector3D);
  size_t matrixSize = 4 * sizeof(QVector4D);
  _vbo.allocate(positionSize + colourSize + matrixSize);
  _vbo.bind();
  _vbo.write(0, triangles.constData(), positionSize);
  _vbo.write(positionSize, colours.constData(), colourSize);

  _colourOffset = positionSize;
  _matrixOffset = positionSize + colourSize;
  _program->setAttributeBuffer(_positionAttr, GL_FLOAT, 0, 3, 0);
  _program->setAttributeBuffer(_colourAttr, GL_FLOAT, _colourOffset, 3, 0);
  _program->setAttributeBuffer(_matrixAttr, GL_FLOAT, _matrixOffset, 4, 4 * sizeof(QVector4D));
  _program->enableAttributeArray(_positionAttr);  
  _program->enableAttributeArray(_colourAttr);
  _program->enableAttributeArray(_matrixAttr);
  _func330->glVertexAttribDivisor(_matrixAttr, 1);
  _vao.release();
  _program->release();
  resizeGL(width(), height());
}

paintGL:

void GLWindow::paintGL()
{
  if (! _context) // not yet initialized
    return;
  _context->makeCurrent(this);
  QColor background(Qt::black);
  glClearColor(background.redF(), background.greenF(), background.blueF(), 1.0f);
  glClearDepth(1.0f);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  QMatrix4x4 matrix;
  matrix.perspective(60, 4.0/3.0, 0.1, 100.0);
  matrix.translate(0, 0, -2);

  _program->bind();
  _vao.bind();
  _vbo.write(_matrixOffset, matrix.constData(), 4 * sizeof(QVector4D));
  /* I tried replacing the three preceding lines with the following, without success: */
  /*
  _vao.bind();
  _vbo.bind();
  _vbo.write(_matrixOffset, matrix.constData(), 4 * sizeof(QVector4D));

  _program->bind();
  _program->enableAttributeArray(_matrixAttr);
  _func330->glVertexAttribDivisor(_matrixAttr, 1); */
  glEnable(GL_DEPTH_TEST);
  _func330->glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 1);
  _vao.release();
  _program->release();
  _context->swapBuffers(this);
  _context->doneCurrent();
}

所以我的实例化问题似乎比在错误的时间有错误的缓冲区边界更大。我还做错了什么?

我认为您必须为位置创建一个VBO,为颜色创建一个VBO(或使用跨距交错数据)。VAO允许您使用多个VBO,每个属性一个。

vao.create();
vao.bind();
// prepare your shader program
// ...
// prepare your VBOs : one VBO for pos, one VBO for colors, one for normals,...
// example for position
vertexPositionBuffer.create();
vertexPositionBuffer.setUsagePattern(QOpenGLBuffer::StaticDraw); 
vertexPositionBuffer.bind();
// if your store the points using QVector<QVector3D>
vertexPositionBuffer.allocate(vertices.constData(), vertices.size() * sizeof(QVector3D));
vertexPositionBuffer.release();
// do the same for colors or other attributes
// ...
// after all buffers are created
shaderProgram.bind();
// Bind the position buffer
vertexPositionBuffer.bind();
shaderProgram.enableAttributeArray("vertexPosition");
shaderProgram.setAttributeBuffer("vertexPosition", GL_FLOAT, 0, 3);
vertexPositionBuffer.release();
// do the same for all other buffers
// ...
shaderProgram.release();
// release vao 
vao.release();
and in your paintGL function:
// update your matrices
// bind your shader program
// set you uniform variables
// then
vao.bind();
glDrawArrays(GL_TRIANGLES, 0, vertices.size());
vao.release();
// release your shader program

我知道了。主要问题是:

  1. 我必须循环遍历mat4属性的所有四列,设置并启用它们中的每一列,并在每一列上调用glVertexAttribDivisor()
  2. 我完全搞砸了QOpenGLShaderProgram::setAttributeBuffer()对我的mat4属性的调用。

本质上,您必须将mat4视为四个单独的vec4属性(每个列一个)。这并不影响你如何将QMatrix4x4数据复制到QOpenGLBuffer对象,只是你如何告诉着色器程序来处理数据。这在我最初的问题链接到的教程和OpenGL编程指南的实例化教程中都有很好的描述,我只是没有得到它。所以,回到上面glwindow.cpp的第一次尝试,我做了很少的改变,现在工作了:

#include "glwindow.h"
#include <QColor>
#include <QMatrix4x4>
#include <QVector>
#include <QVector3D>
#include <QVector4D>
#include <QDebug>

GLWindow::GLWindow(QWindow *parent) 
  : QWindow(parent)
  , _vbo(QOpenGLBuffer::VertexBuffer)
  , _matbo(QOpenGLBuffer::VertexBuffer)
  , _context(0)
{
  setSurfaceType(QWindow::OpenGLSurface);
}
GLWindow::~GLWindow()
{}
void GLWindow::initGL()
{  
  setupShaders();
  _program->bind();
  _positionAttr = _program->attributeLocation("position");
  _colourAttr = _program->attributeLocation("colour");
  _matrixAttr = _program->attributeLocation("matrix");
  QVector<QVector3D> triangles;
  triangles << QVector3D(-0.5, 0.5, 1) << QVector3D(-0.5, -0.5, 1) << QVector3D(0.5, -0.5, 1);
  triangles << QVector3D(0.5, 0.5, 0.5) << QVector3D(-0.5, -0.5, 0.5) << QVector3D(0.5, -0.5, 0.5);
  QVector<QVector3D> colours;
  colours << QVector3D(1, 0, 0) << QVector3D(0, 1, 0) << QVector3D(0, 0, 1);
  colours << QVector3D(1, 1, 1) << QVector3D(1, 1, 1) << QVector3D(1, 1, 1);
  _vao.create();
  _vao.bind();
  _vbo.create();
  _vbo.setUsagePattern(QOpenGLBuffer::StaticDraw);
  _vbo.bind();
  size_t positionSize = triangles.size() * sizeof(QVector3D);
  size_t colourSize = colours.size() * sizeof(QVector3D);
  _vbo.allocate(positionSize + colourSize);
  _vbo.bind();
  _vbo.write(0, triangles.constData(), positionSize);
  _vbo.write(positionSize, colours.constData(), colourSize);
  _colourOffset = positionSize;
  _program->setAttributeBuffer(_positionAttr, GL_FLOAT, 0, 3, 0);
  _program->setAttributeBuffer(_colourAttr, GL_FLOAT, _colourOffset, 3, 0);
  _program->enableAttributeArray(_positionAttr);  
  _program->enableAttributeArray(_colourAttr);

  _matbo.create();
  _matbo.setUsagePattern(QOpenGLBuffer::StaticDraw);
  _matbo.bind();
  _matbo.allocate(4 * sizeof(QVector4D));
  // This is completely wrong
  /*_program->setAttributeBuffer(_matrixAttr, GL_FLOAT, 0, 4, 4 * sizeof(QVector4D));
  _program->enableAttributeArray(_matrixAttr);
  _func330->glVertexAttribDivisor(_matrixAttr, 1);
  */      
  // The right way to set up a mat4 attribute for instancing
  for (unsigned i = 0; i < 4; i++)
  {
    _program->setAttributeBuffer(_matrixAttr + i, GL_FLOAT, i * sizeof(QVector4D), 4, 4 * sizeof(QVector4D));
    _program->enableAttributeArray(_matrixAttr + i);
    _func330->glVertexAttribDivisor(_matrixAttr + i, 1);
  }
  _matbo.release();
  _vao.release();
  _program->release();
  resizeGL(width(), height());
}
void GLWindow::resizeGL(int w, int h)
{
  glViewport(0, 0, w, h);
}
void GLWindow::paintGL()
{
  if (! _context) // not yet initialized
    return;
  _context->makeCurrent(this);
  QColor background(Qt::black);
  glClearColor(background.redF(), background.greenF(), background.blueF(), 1.0f);
  glClearDepth(1.0f);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  QMatrix4x4 matrix;
  matrix.perspective(60, 4.0/3.0, 0.1, 100.0);
  matrix.translate(0, 0, -2);
  _program->bind();
  _vao.bind();
  _matbo.bind();
  _matbo.write(0, matrix.constData(), 4 * sizeof(QVector4D));

  glEnable(GL_DEPTH_TEST);
  _func330->glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 1);
  _vao.release();
  _program->release();
  _context->swapBuffers(this);
  _context->doneCurrent();
}
void GLWindow::setupShaders()
{
  QString vShaderSrc("#version 330n"
    "layout(location = 0) in vec4 position;n"
    "layout(location = 1) in vec4 colour;n"
    "layout(location = 2) in mat4 matrix;n"
    "smooth out vec4 col;n"
    "void main() {n"
    "   col = colour;n"
    "   gl_Position = matrix * position;n"
    "}n");
  QString fShaderSrc("#version 330n"
    "smooth in vec4 col;n"
    "void main() {n"
    "   gl_FragColor = col;n"
    "}n");

  _program = new QOpenGLShaderProgram(this);
  _program->addShaderFromSourceCode(QOpenGLShader::Vertex, vShaderSrc);
  _program->addShaderFromSourceCode(QOpenGLShader::Fragment, fShaderSrc);
  _program->link();

}
void GLWindow::exposeEvent(QExposeEvent *event)
{
  Q_UNUSED(event);
  if (isExposed())
  {
    if (! _context)
    {
      _context = new QOpenGLContext(this);
      QSurfaceFormat format(requestedFormat());
      format.setVersion(3,3);
      format.setDepthBufferSize(24);
      _context->setFormat(format);
      _context->create();
      _context->makeCurrent(this);
      initializeOpenGLFunctions();
      _func330 = _context->versionFunctions<QOpenGLFunctions_3_3_Core>();
      if (_func330)
        _func330->initializeOpenGLFunctions();
      else
      {
        qWarning() << "Could not obtain required OpenGL context version";
        exit(1);
      }
      initGL();
    }
    paintGL();
  }
}

注意,我还移动了_matbo的绑定和mat4属性的设置,以便在释放VAO之前完成所有操作。我最初对允许多少vbo以及何时需要绑定它们感到非常困惑。在单个VAO内拥有多个vbo没有问题,只是需要将正确的vbo绑定到写入,并且在调用QOpenGLShaderProgram::setAttributeBuffer()之前需要将正确的vbo绑定。当glDraw*()被调用时,哪个缓冲区被绑定并不重要(我相信如果我错了,有人会评论)。

相关内容

  • 没有找到相关文章

最新更新