这是真实感图像渲染系列的第七篇文章。
如果将渲染修改为并行,那么渲染效率将提升约8倍(我的电脑是八核)。非常非常有诱惑力。而且基于OpenMP框架的并行化甚至还非常简单!
平台编译
使用OpenMP最麻烦的地方是框架安装问题,倘若本机的g++版本支持-fopenmp ,那么恭喜你,你已经完成了90%的工作了。倘若本机g++不支持该编译选项,那么需要在网上搜索对应的安装方法。安装方法不同系统都有较大的差别,故不在这里细讲。
OpenMP程序并行化指令都是用#pragma 实现的,也就是说倘若编译器不支持,也不会产生什么问题。
OpenMP简单使用
这是一个最简单的for语句,对应于PPM算法中的光子映射。
for (int i=0;i<total_photons;i++) { PhotonTracing(...); }
该for语句满足如下条件:
- 循环起始位置和终结位置确定
- 不同的循环没有依赖性,
满足这样条件的for语句,理论上,将循环不同的i并行处理是没有问题的。OpenMP使用了最简单的预编译指令帮助我们实现for语句的并行,修改为
#include <omp.h> #pragma omp parallel for for (int i=0;i<total_photons;i++) { PhotonTracing(...); }
OpenMP临界区设置
#pragma omp critical 编译器将自动将#pragma 指令后面紧接着的语句块进行并行。
一般来说,并行语句块内使用的变量,都是读写局部变量以及读全局变量,这时,不会存在什么问题,但是总有特殊情况,我们多个并行的循环需要修改同一个全局变量。这是可能导致错误的。如PPM算法中,计算“视点”,需要将结果保存在全局数组view_pts 里面。这时,多个线程并行修改会导致一些问题(诸如SegmentationFault)。这时,使用#pragma omp critical 可以强制后面紧接着的那个语句块通过“加锁”保证同时只有一个线程在运行该代码块。
if (obj->getMaterial().diff > feps) { #pragma omp critical view_pts.push_back( ViewPoint( obj_coll.getSurfaceC(), obj_coll.N, rayC*obj->getColor(obj_coll.C), lambda*obj->getMaterial().diff, xx,yy ) ); }