我使用的是QT 4.8,Visual Studio 2008和OpenGL 3.3
我有两个需要在OpenGL中渲染(静态)的数据集。我正在使用QThread从我的QGLWidget实现一个单独的显示线程。 如果我先将两个数据集加载到主内存中,然后启动渲染线程,一切都运行正常,但我现在需要的是为用户提供加载和卸载第二个数据集的灵活性,而不会影响第一个数据集。 所以我有一个按钮来加载第一个数据集:
void MyView::on_btn_start_clicked()
{
// Load first data set from file and start the render thread
// MyGLObject is a QGLWidget object
MyGLObject->Data1 = new DataSet1();
if(MyGLObject->Data1->processCSVFile("filename.csv"))
{
MyGLObject->initRenderThread();
}
}
这是iniRenderThread()的作用:
void MyGLWidget::initRenderThread()
{
doneCurrent();
displayThread.initModelRender();
displayThread.start();
}
displayThread是QThread的子类,run()方法的类似于:
void DisplayThread::run()
{
MyGLObject->makeCurrent();
// OPenGL and GLEW initialization here ....
...
// initialize first data set's VAO and VBO
glGenVertexArrays(1, &Vao_data1);
glBindVertexArray(Vao_data1);
glGenBuffers(1, &Vbo_data1);
glBindBuffer(GL_ARRAY_BUFFER, Vbo_data1);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
glBindVertexArray(0);
// Only generate VAO and VBO for second dataset (no data there yet)
glGenVertexArrays(1, &Vao_data2);
glBindVertexArray(Vao_data2);
glGenBuffers(1, &Vbo_data2);
glBindVertexArray(0);
// load shader here
...
while(doRendering)
{
if(doResize)
{
GLResize(w, h);
doResize = false;
}
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// setup model view matrix and pass it as shader uniform
...
// Draw first dataset
glBindVertexArray(Vao_data1);
glDrawArrays(GL_POINTS, 0, NUMVERTS);
// check if second set is loaded and draw if it is
if(secondDataLoaded)
{
glBindVertexArray(Vao_data2);
glDrawArrays(GL_LINE_STRIP, start, (end - start));
}
MyGLObject->swapBuffers();
if(!benchmark)
{
Mutex.lock();
updateFrame = false;
if(!updateFrame && doRendering && !reloadShader)
WaitCondition.wait(&Mutex);
Mutex.unlock();
}
frameCounter++;
} // end while rendering
}
现在重要的问题是:在哪里为第二个数据集调用glBufferData()?????? 我尝试在DisplyThread类的一个插槽中调用它,以响应按钮单击发出调用信号。 所以显示线程中的一个插槽就像:
DisplayThread::showSecondSet()
{
glBindVertexArray(Vao_data2);
glBindBuffer(GL_ARRAY_BUFFER, Vbo_data2);
glBufferData(GL_ARRAY_BUFFER, sizeof(dataSet2->vertices), dataSet2->vertices, GL_STATIC_DRAW);
secondDataLoaded = true;
}
渲染中没有出现。我也通过在插槽中分配内存然后在渲染循环中调用glBufferSubData()来尝试解决方案here,但仍然没有得到任何结果。 它正确呈现的唯一方法是,如果我在render循环中调用glBufferData(),这是非常低效的。 无论如何,我只能在执行插槽时创建VBO并将数据复制到它,而不是在渲染循环内部吗?
编辑:我尝试使用qDebug()从displayThread()中的showSecondSet()SLOT打印一些东西,它正在打印,并且插槽内的所有其他函数调用正在执行,除了OpenGL调用。我猜它与当前的GL背景有关答案 0 :(得分:0)
将VBO更新放入插槽是正确的做法。但是,您还必须确保在目标上下文处于活动状态时执行插槽,这在大多数情况下归结为在DisplayThread中执行的插槽。
在其中一个项目中我不得不处理这个问题,最后我通过一堆蹦床插槽和QMetaObject::invoke
来电调用。例如,在生产代码中有以下内容:
class … {
public slots:
void doFoobar(
unsigned int a,
unsigned int b ) {
QMetaObject::invokeMethod(this, "_priv_loslot_doFoobar",
Qt::QueuedConnection,
Q_ARG(unsigned int, a),
Q_ARG(unsigned int, b) );
}
private slots:
void _priv_loslot_doFoobar(
unsigned int a,
unsigned int b );
};
是的,它是粗糙的,是的,它是一种解决方法,但它完成了工作。请注意,这样做就像你正在尝试的那样非常相似:在OpenGL缓冲区对象中实时更新数据,由具有自己的OpenGL上下文的工作线程执行。
渲染中没有出现
只更新VBO是不够的。 OpenGL不是一个场景图,VBO本身只是一块没有形式的内存。您需要调用完整的重绘以使更改可见。
答案 1 :(得分:0)
我建议创建一个新的QObject并将while的主体移动到该对象的一个插槽中,并使用QTimer让它重复:
void DisplayHandler::started()//slot connected to the running's QThread's started
{
MyGLObject->makeCurrent();
// OPenGL and GLEW initialization here ....
...
// initialize first data set's VAO and VBO
glGenVertexArrays(1, &Vao_data1);
glBindVertexArray(Vao_data1);
glGenBuffers(1, &Vbo_data1);
glBindBuffer(GL_ARRAY_BUFFER, Vbo_data1);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
glBindVertexArray(0);
// Only generate VAO and VBO for second dataset (no data there yet)
glGenVertexArrays(1, &Vao_data2);
glBindVertexArray(Vao_data2);
glGenBuffers(1, &Vbo_data2);
glBindVertexArray(0);
// load shader here
...
}
void DisplayHandler::draw()//slot called when update needs to happen
{
MyGLObject->makeCurrent();
if(doResize)
{
GLResize(w, h);
doResize = false;
}
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// setup model view matrix and pass it as shader uniform
...
// Draw first dataset
glBindVertexArray(Vao_data1);
glDrawArrays(GL_POINTS, 0, NUMVERTS);
// check if second set is loaded and draw if it is
if(secondDataLoaded)
{
glBindVertexArray(Vao_data2);
glDrawArrays(GL_LINE_STRIP, start, (end - start));
}
MyGLObject->swapBuffers();
MyGLObject->doneCurrent();
frameCounter++;
}
void DisplayHandler::showSecondSet()
{
MyGLObject->makeCurrent();
glBindVertexArray(Vao_data2);
glBindBuffer(GL_ARRAY_BUFFER, Vbo_data2);
glBufferData(GL_ARRAY_BUFFER, sizeof(dataSet2->vertices), dataSet2->vertices, GL_STATIC_DRAW);
secondDataLoaded = true;
MyGLObject->doneCurrent();
}
然后开始线程:
void MyGLWidget::initRenderThread()
{
doneCurrent();
QThread* thr = new QThread(this);
connect(thr, &QThread::started, &displayThread, &DisplayHandler::started);
connect(this, &QObject::destroyed, thr, &QThread::quit);
displayThread.moveToThread(thr);
displayThread.initModelRender();
thr.start();
}
答案 2 :(得分:0)
我通过将绘制命令中的VBO调用(glGufferData()调用)分开来解决了这个问题。 我现在保留2个名为“secondDataLoaded”和“doReload”的布尔变量 将第二个数据集加载到主机内存后,应用程序将发出一个连接到showSecondSet()插槽的信号 现在showSecondSet()的主体是这样的:
DisplayThread::showSecondSet()
{
secondDataLoaded = true;
doReload = true;
}
在渲染循环中,我有:
if(secondDataLoaded)
{
glBindVertexArray(Vao_data2);
if(doReload)
{
createVBOs();
doReload = false;
}
drawData2();
glBindVertexArray(0);
}
像魅力一样工作:)感谢大家的时间和宝贵的提示。