最近本人在写离线光线追踪渲染器,但是Qt::QtConcurrent的功能有限,所以就尝试使用了一下,顺便分享一些经验。
TBB里面的parallel_for非常适合光线追踪渲染器,而QtConcurrent没有这个函数
平台
- Qt:Qt 5.9.4 MSVC x64
- TBB:Threading Building Blocks 2018 Update 5
- CPU:inter i7-3630QM
配置TBB
首先去https://www.threadingbuildingblocks.org/下载TBB,之后会链接到github:https://github.com/01org/tbb/releases。选择对应平台下载。
文件说明
bin:里面的ia32与inter64文件夹分别放着x32与x64的dll文件。虽然网上的教程(配置VS)有所以设置path环境变量与启动路径里设置tbbvars.bat。但是QtCreator貌似做不到这些。
include:c++头文件
lib:里面的ia32与inter64文件夹分别放着x32与x64的lib文件
配置过程
- 添加库文件:在Qt项目栏中(显示所有工程文件的那栏)的工程图标中右键-添加库-外部库,选择库文件(tbb.lib)。之后在pro文件中将debug模式的文件名从-ltbbd改成-ltbb_debug。
- 添加头文件:在pro中加入INCLUDEPATH += $$PWD/XXXX/include,也就是tbb里的include文件夹路径。
- 执行QMake
- 将bin里对应平台的文件放到生成程序的目录下,即完成所有配置工作。
我的配置
这里我直接新建了一个名为“tbb”的文件夹放在工程目录里了。
INCLUDEPATH += $$PWD/tbb/include
win32:CONFIG(release, debug|release): LIBS += -L$$PWD/tbb/lib/intel64/vc14/ -ltbb
else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/tbb/lib/intel64/vc14/ -ltbb_debug
INCLUDEPATH += $$PWD/tbb/lib/intel64/vc14
DEPENDPATH += $$PWD/tbb/lib/intel64/vc14
需要注意的细节
tbb的任务组会使Qt的信号槽不正常,此时把任务组换成QtConcurrent::run,信号槽正常运行。
task_group g;
g.run(
//QtConcurrent::run(
[this](){
Point2D sp;//采样点坐标
Point2D pp;//pixel上的采样点
int nx = setting->imageWidth;
int ny = setting->imageHeight;
int allPixelNum=nx*ny;
int currentPixelNum=0;
Vector3D lower_left_corner(-2.0, -1.0, -1.0);
Vector3D horizontal(4.0, 0.0, 0.0);
Vector3D vertical(0.0, 2.0, 0.0);
Point3D origin(0.0, 0.0, 0.0);
tbb::parallel_for( tbb::blocked_range2d<int>(0, nx, 1, 0, ny, 1),
[&](const tbb::blocked_range2d<int>& r)
{
for( int i=r.rows().begin(); i!=r.rows().end(); ++i ){
for( int j=r.cols().begin(); j!=r.cols().end(); ++j ) {
RGBColor pixelColor;
Ray ray;
for(int k=0;k<setting->numSamples;k++){
sp=setting->samplerPtr->sampleUnitSquare();
float u = float(i+sp.x) / float(nx);
float v = float(j+sp.y) / float(ny);
ray.origin=origin;
ray.direction=lower_left_corner+u*horizontal+v*vertical;
pixelColor+= tracer_ptr->trace_ray(ray);
}
pixelColor/=setting->numSamples;
currentPixelNum++;
emit pixelComplete(i,j,currentPixelNum*100/allPixelNum,QColor( int(255.99*pixelColor.r), int(255.99*pixelColor.g), int(255.99*pixelColor.b)));
}
}
});
g.wait();
emit renderComplete();});
}
以上代码是在渲染完一个像素后通过信号槽传递到GUI上,更新结果显示,这样做很可能会降低渲染的效率(因为信号槽的机制,这也是Qt的网络库不行的原因之一)。不过渲染每像素时间超过一定量的时候,这种损耗也可以忽略不计(1min以内渲染1024*1024这种明显会降低渲染效率)。
不过因为本人的渲染(测试)的时间不会超过10min,而且可以通过渲染经过时间与像素总数的比值来调整GUI线程更新渲染结果的间隔,从而解决因长时间渲染(30Min以上),GUI线程的无效更新而造成的损耗。