这是真实感图像渲染系列的第六篇文章。
动机
一幅图片渲染时间少则一分钟,多则半天,等到渲染完成再看效果未免也太佛系了。况且诸如PPM算法亮度调节什么的一般在前两轮的时候,就已经大概知道了,并不需要渲染完。因此我们希望写一个实时显示渲染效果的类。
实现
显示部分在技术上没有任何难度,就是OpenCV中的imshow而已,如果必要,每次imshow之前,使用imwrite备份图片到文件。
我们需要一边显示一遍渲染,这个就需要多线程了。由于opencv的限制,imshow和waitKey只能在主线程调用,因此我们把图像渲染放在子线程里面去。这里线程的创建我使用了pthreads库。
具体细节就看代码啦。init函数传入一个Color类数组的指针,而PaintBoard能够实时监测该指针指向内容的变化,并且显示出来。(需要用户在窗口按回车键刷新)
#ifndef PAINT_BOARD_H #define PAINT_BOARD_H #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include "../core/color.h" using namespace std; class PaintBoard { private: cv::Mat *image; Color **board; int sizX,sizY; public: PaintBoard(); ~PaintBoard(); void init(int sizX,int sizY,Color** _board); void update(); void display(); void save(); void save_raw(); }; #endif
#include "paint_board.h" #include "glog/logging.h" #include <iostream> using namespace cv; PaintBoard::PaintBoard() { sizX = -1,sizY = -1; image = NULL; } PaintBoard::~PaintBoard() { } void PaintBoard::init(int sizX,int sizY,Color** _board) { this->sizX = sizX; this->sizY = sizY; image = new Mat(this->sizX,this->sizY,CV_64FC3,Scalar(1.0,1.0,1.0)); board = _board; } void PaintBoard::update() { assert(image); for (int i=0;i<sizX;i++) { for (int j=0;j<sizY;j++) { //Color& c = (*board)[(sizX-i-1)*sizY+j]; Color& c = (*board)[i*sizY+j]; Vec3f v(c.getR(),c.getG(),c.getB()); image->at<Vec3d>(i,j) = v; } } } void PaintBoard::display() { imshow("picture", *image); waitKey(0); } void PaintBoard::save_raw() { FILE* fout = fopen("result.ppm","w"); fprintf(fout,"P3 %d %d\n",sizY,sizX); fprintf(fout,"255\n"); for (int i=0;i<sizX;i++) { for (int j=0;j<sizY;j++) { Color c = (*board)[i*sizY+j]*255; fprintf(fout,"% 3d % 3d % 3d",int(c.getR()),int(c.getG()),int(c.getB())); } fprintf(fout,"\n"); } fclose(fout); } void PaintBoard::save() { vector<int>compression_params; compression_params.push_back(IMWRITE_PNG_COMPRESSION); compression_params.push_back(9); imwrite("result.bmp", (*image)*255); }
#include <iostream> #include "display/paint_board.h" #include "json/json.h" #include "render/raytracing.h" #include "render/progressive_photon_mapping.h" #include <unistd.h> #include <fstream> #include <pthread.h> bool show_graph_flag = true; void* show_graph(void* params) { Render *PPM = (Render*)params; PPM->run(); show_graph_flag = false; return NULL; } int main(int argc, char** args) { std::ifstream ifs("configures/config.json"); Json::CharReaderBuilder reader; Json::Value root; JSONCPP_STRING errs; Json::parseFromStream(reader, ifs, &root, &errs); Render *PPM = new ProgressivePhotonMapping(); //Render *PPM = new RayTracing(); PaintBoard PB; PPM->accept(root); PPM->registerPaintBoard(&PB); pthread_t watcher; pthread_create(&watcher,NULL,show_graph,PPM); while (show_graph_flag) { PB.update(); PB.save(); PB.display(); usleep(100000); } show_graph_flag = false; pthread_join(watcher,NULL); PB.update(); PB.save(); }