近段時間做一個關於水面的動畫。由於我用c++實現水面動畫的,然而使用c++我自己的渲染系統渲染結果被同學說是可視化不叫渲染,所以我決定修改一下……
恰好進來在學習pbrt,所以索性就蛋疼了考慮直接用pbrt來渲染吧……(至於為什么,僅為好玩兒……)
pbrt默認的渲染方式是使用一個場景描述文件.pbrt,我要渲染的對象是三角網絡就必須使用對應的描述語句定義三角對象:
Shape "trianglemesh"
"integer indices" [0 2 1 ]
"point P" [0 0 0.401925 2 0 0.92604 0 2 0.950944 ]
"normal N" [-0.245007 -0.256649 0.934935 -0.439362 -0.242124 0.865065 -0.228464 -0.466945 0.854264 ]
對應的參數都很清晰不多說了,我的方法就是將我的三角網絡拆成三角形按照這個格式一個一個輸出到文件,mesh.pbrt:
Shape "trianglemesh" "integer indices" [0 2 1 ] "point P" [0 0 0.401925 2 0 0.92604 0 2 0.950944 ] "normal N" [-0.245007 -0.256649 0.934935 -0.439362 -0.242124 0.865065 -0.228464 -0.466945 0.854264 ] Shape "trianglemesh" "integer indices" [0 2 1 ] "point P" [2 0 0.92604 2 2 1.48582 0 2 0.950944 ] "normal N" [-0.439362 -0.242124 0.865065 -0.387612 -0.436964 0.811677 -0.228464 -0.466945 0.854264 ] Shape "trianglemesh" "integer indices" [0 2 1 ] "point P" [2 0 0.92604 4 0 1.41771 2 2 1.48582 ] "normal N" [-0.439362 -0.242124 0.865065 -0.371551 -0.220212 0.901918 -0.387612 -0.436964 0.811677 ]
......
按照pbrt給出的例子寫出場景主文件:
#定義表面積分器 SurfaceIntegrator "directlighting" #transform ConcatTransform [ 0.828849 -0.295370 -0.475149 0.000000 -0.559473 -0.437585 -0.703924 0.000000 -0.000000 0.849280 -0.527943 0.000000 0.000000 0.000000 0.000000 1.000000 ] Translate -4.860000 -7.200000 -5.400000 #定義攝像機 Camera "perspective" "float fov" [90.00000 ] "float shutteropen" [0.000000 ] "float shutterclose" [0.000000 ] "float screenwindow" [-1.000000 1.000000 -1.000000 1.000000 ] "float frameaspectratio" [1.333333 ] #定義膠片 Film "image" "integer xresolution" [200 ] "integer yresolution" [200 ] "string filename" "1.exr" #定義采樣器 Sampler "lowdiscrepancy" "integer pixelsamples" [8] PixelFilter "box" WorldBegin AttributeBegin LightSource "infinite" "string mapname" ["textures/skylight-dusk.exr"] "color L" [0.5 0.5 0.5] "integer nsamples" [16] AttributeEnd AttributeBegin #mesh的材質 Material "uber" "color Kd" [0 0.1 0.15] "color Kr" [0.9 0.9 0.9] "color Ks" [0.1 0.1 0.1] "float roughness" [0.9] "float index" [1.34] #mesh trnsform Translate -90 -80 -2 Scale 0.1 0.1 0.3 #包含mesh Include "mesh.pbrt" AttributeEnd WorldEnd
(更多的pbrt場景命令你可以去官網下文檔來看)
執行下列命令(當然你要先編譯好pbrt,這個直接下pbrt-v2編譯就好,沒什么需要說明的地方):
pbrt scene.pbrt
執行pbrt就會出現一個進度條開始渲染對象,渲染完成后就會在當前目錄輸出一個1.exr圖像。
但是我要做的是動畫,每幀mesh有至少有N*N*2個三角面,N>=512,我的動畫打算30秒,20幀,一共600張圖,需要600個mesh,這個要是手動來一個一個搞,別說mesh輸出一共要600*100M+,就是人也要累死啊。
考慮用寫個批處理來搞一下感覺也不好使,后來反正因為學習pbrt就決定把pbrt的代碼嵌入到c++程序中去算了。
pbrt代碼本身唯一的文檔,就是pbrt那本書了,但是型號它的API很清晰易懂,幾乎每個api都是場景描述文件對應的,所以我只需要把場景pbrt文件翻譯成c++代碼就可以調用pbrt了,pbrt的API如下:
void pbrtInit(const Options &opt); void pbrtCleanup(); void pbrtIdentity(); void pbrtTranslate(float dx, float dy, float dz); void pbrtRotate(float angle, float ax, float ay, float az); void pbrtScale(float sx, float sy, float sz); void pbrtLookAt(float ex, float ey, float ez,float lx, float ly, float lz,float ux, float uy, float uz); void pbrtConcatTransform(float transform[16]); void pbrtTransform(float transform[16]); void pbrtCoordinateSystem(const string &); void pbrtCoordSysTransform(const string &); void pbrtActiveTransformAll(); void pbrtActiveTransformEndTime(); void pbrtActiveTransformStartTime(); void pbrtTransformTimes(float start, float end); void pbrtPixelFilter(const string &name, const ParamSet ¶ms); void pbrtFilm(const string &type, const ParamSet ¶ms); void pbrtSampler(const string &name, const ParamSet ¶ms); void pbrtAccelerator(const string &name, const ParamSet ¶ms); void pbrtSurfaceIntegrator(const string &name, const ParamSet ¶ms); void pbrtVolumeIntegrator(const string &name, const ParamSet ¶ms); void pbrtRenderer(const string &name, const ParamSet ¶ms); void pbrtCamera(const string &, const ParamSet &cameraParams); void pbrtWorldBegin(); void pbrtAttributeBegin(); void pbrtAttributeEnd(); void pbrtTransformBegin(); void pbrtTransformEnd(); void pbrtTexture(const string &name, const string &type,const string &texname, const ParamSet ¶ms); void pbrtMaterial(const string &name, const ParamSet ¶ms); void pbrtMakeNamedMaterial(const string &name, const ParamSet ¶ms); void pbrtNamedMaterial(const string &name); void pbrtLightSource(const string &name, const ParamSet ¶ms); void pbrtAreaLightSource(const string &name, const ParamSet ¶ms); void pbrtShape(const string &name, const ParamSet ¶ms); void pbrtReverseOrientation(); void pbrtVolume(const string &name, const ParamSet ¶ms); void pbrtObjectBegin(const string &name); void pbrtObjectEnd(); void pbrtObjectInstance(const string &name); void pbrtWorldEnd();
對比一下場景描述文件,發現基本一一對應。
pbrt本身是被編譯成lib靜態庫的,而pbrt.exe僅僅是一個引用pbrtlib的單文件c++程序而已:
#include "stdafx.h" #include "api.h" #include "probes.h" #include "parser.h" #include "parallel.h" // main program int main(int argc, char *argv[]) { Options options; vector<string> filenames; // Process command-line arguments for (int i = 1; i < argc; ++i) { if (!strcmp(argv[i], "--ncores")) options.nCores = atoi(argv[++i]); else if (!strcmp(argv[i], "--outfile")) options.imageFile = argv[++i]; else if (!strcmp(argv[i], "--quick")) options.quickRender = true; else if (!strcmp(argv[i], "--quiet")) options.quiet = true; else if (!strcmp(argv[i], "--verbose")) options.verbose = true; else if (!strcmp(argv[i], "--help") || !strcmp(argv[i], "-h")) { printf("usage: pbrt [--ncores n] [--outfile filename] [--quick] [--quiet] " "[--verbose] [--help] <filename.pbrt> ...\n"); return 0; } else filenames.push_back(argv[i]); } // Print welcome banner if (!options.quiet) { printf("pbrt version %s of %s at %s [Detected %d core(s)]\n", PBRT_VERSION, __DATE__, __TIME__, NumSystemCores()); printf("Copyright (c)1998-2014 Matt Pharr and Greg Humphreys.\n"); printf("The source code to pbrt (but *not* the book contents) is covered by the BSD License.\n"); printf("See the file LICENSE.txt for the conditions of the license.\n"); fflush(stdout); } pbrtInit(options); // Process scene description PBRT_STARTED_PARSING(); if (filenames.size() == 0) { // Parse scene from standard input ParseFile("-"); } else { // Parse scene from input files for (u_int i = 0; i < filenames.size(); i++) if (!ParseFile(filenames[i])) Error("Couldn't open scene file \"%s\"", filenames[i].c_str()); } pbrtCleanup(); return 0; }
其中起到解析文件作用就是如下這段:
pbrtInit(options); // Process scene description PBRT_STARTED_PARSING(); if (filenames.size() == 0) { // Parse scene from standard input ParseFile("-"); } else { // Parse scene from input files for (u_int i = 0; i < filenames.size(); i++) if (!ParseFile(filenames[i])) Error("Couldn't open scene file \"%s\"", filenames[i].c_str()); }
它是使用yacc解析的,細節我並不懂,我把這個文件換成如下:
#include "stdafx.h" #include "api.h" #include "probes.h" #include "parser.h" #include "parallel.h" #include "paramset.h" extern void InitParamSet(ParamSet &ps, SpectrumType); void create_a_tri( Point i1,Point i2,Point i3, Normal n1,Normal n2,Normal n3 ) { ParamSet params; //mesh參數 int s[3] = {0,2,1}; params.AddInt("indices",s,3); Point p[3] = {i1,i2,i3}; params.AddPoint("P",p,3); Normal n[3] = {n1,n2,n3}; params.AddNormal("N",n,3); pbrtShape("trianglemesh",params); } int main(int argc, char *argv[]) { Options opt; opt.imageFile = "1.exr"; opt.nCores = 4; opt.openWindow = false; opt.quickRender = false; pbrtInit(opt); ParamSet params; /* InitParamSet(params, SPECTRUM_REFLECTANCE); pbrtSurfaceIntegrator("directlighting",params); */ //初始化所有參數 //表面積分器參數 int maxdepth = 5; params.AddInt("maxdepth",&maxdepth,1); string strategy = "all"; params.AddString("strategy",&strategy,1); //相機參數 float fov = 90; params.AddFloat("fov",&fov,1); float shutteropen = 0.00f; params.AddFloat("shutteropen",&shutteropen,1); float shutterclose = 0.00f; params.AddFloat("shutterclose",&shutterclose,1); float screenwindow[4] = {-1.0,1.0,-1.0,1.0}; params.AddFloat("screenwindow",screenwindow,4); float ratio = 1.333333; params.AddFloat("frameaspectratio",&ratio,1); //膠片 int xresolution = 640; int yresolution = 480; string filename = "1.exr"; params.AddInt("xresolution",&xresolution,1); params.AddInt("yresolution",&yresolution,1); params.AddString("filename",&filename,1); //采樣器 int pixelsample = 8; params.AddInt("pixelsamples",&pixelsample,1); //過濾器 float xwidth = 0.5; float ywidth = 0.5; params.AddFloat("xwith",&xwidth,1); params.AddFloat("ywith",&ywidth,1); //光照 float L[3] = {0.5,0.5,0.5}; int nsamples = 16; string mapname = "textures/skylight-dusk.exr"; params.AddRGBSpectrum("L",L,3); params.AddInt("nsamples",&nsamples,1); params.AddString("mapname",&mapname,1); //材質 float Kd[3] = {0,0.1,0.15}; float Kr[3] = {0.9,0.9,0.9}; float Ks[3] = {0.1,0.1,0.1}; float roughness = 0.9; float index = 1.34; params.AddRGBSpectrum("Kd",Kd,3); params.AddRGBSpectrum("Kr",Kr,3); params.AddRGBSpectrum("Ks",Ks,3); params.AddFloat("roughness",&roughness,1); params.AddFloat("index",&index,1); pbrtSurfaceIntegrator("directlighting",params); float ct[16] = {0.828849,-0.295370,-0.475149,0.000000,-0.559473,-0.437585,-0.703924,0.000000,-0.000000,0.849280,-0.527943,0.000000,0.000000,0.000000,0.000000,1.000000}; pbrtConcatTransform(ct); pbrtTranslate(-4.860000,-7.200000,-5.400000); pbrtCamera("perspective",params); pbrtFilm("image",params); pbrtSampler("lowdiscrepancy",params); pbrtPixelFilter("box",params); pbrtWorldBegin(); pbrtAttributeBegin(); pbrtLightSource("infinite",params); pbrtAttributeEnd(); pbrtAttributeBegin(); pbrtMaterial("uber",params); //pbrtTranslate(-90,-80,-2); //pbrtScale(0.1,0.1,0.4); pbrtTranslate(-1,-1,-9); pbrtScale(1,1,1); //mesh參數 int s[3] = {0,2,1}; params.AddInt("indices",s,3); Point p[3] = {Point(0,0,11.8604),Point(2,0,11.2012),Point(0,2,11.2039)}; params.AddPoint("P",p,3); Normal n[3] = {Normal(13.1851,13.1312,40),Normal(25.1371,13.4084,40),Normal(13.4624,25.8694,40)}; params.AddNormal("N",n,3); pbrtShape("trianglemesh",params); create_a_tri(Point(0,0,11.8604),Point(2,0,11.2012),Point(0,2,11.2039),Normal(13.1851,13.1312,40),Normal(25.1371,13.4084,40),Normal(13.4624,25.8694,40)); create_a_tri(Point(2,0,11.2012),Point(2,2,10.5308),Point(0,2,11.2039),Normal(25.1371,13.4084,40),Normal(21.2989,23.2563,40),Normal(13.4624,25.8694,40)); pbrtAttributeEnd(); pbrtWorldEnd(); pbrtCleanup(); return 0; }
很好,和pbrt文件渲染出來類似結果。
有了這些就可以把它嵌入到of中了,定義兩個函數:
void create_a_tri( Point i1,Point i2,Point i3, Normal n1,Normal n2,Normal n3 ); int render(const char* file_name,FloatPixels** pxs,int N,float LL,float lamda,float nla);
第一個創建一個三角形,第二個就是使用c++創建pbrt場景渲染輸出。實現一下:
#include "render.h" #include "core\stdafx.h" #include "core\api.h" #include "core\probes.h" #include "core\parser.h" #include "core\parallel.h" #include "core\paramset.h" void create_a_tri( Point i1,Point i2,Point i3, Normal n1,Normal n2,Normal n3 ) { ParamSet params; //mesh參數 int s[3] = {0,2,1}; params.AddInt("indices",s,3); Point p[3] = {i1,i2,i3}; params.AddPoint("P",p,3); Normal n[3] = {n1,n2,n3}; params.AddNormal("N",n,3); pbrtShape("trianglemesh",params); } int render(const char* file_name,FloatPixels** pxs,int N,float LL,float lamda,float nla) { printf("Generating Mesh...\n"); Options opt; opt.nCores = 4; opt.openWindow = false; opt.quickRender = false; pbrtInit(opt); ParamSet params; //初始化所有參數 //表面積分器參數 int maxdepth = 5; params.AddInt("maxdepth",&maxdepth,1); string strategy = "all"; params.AddString("strategy",&strategy,1); //相機參數 float fov = 120; params.AddFloat("fov",&fov,1); float shutteropen = 0.00f; params.AddFloat("shutteropen",&shutteropen,1); float shutterclose = 0.00f; params.AddFloat("shutterclose",&shutterclose,1); float screenwindow[4] = {-1.0,1.0,-1.0,1.0}; params.AddFloat("screenwindow",screenwindow,4); float ratio = 1.333333; params.AddFloat("frameaspectratio",&ratio,1); //膠片 int xresolution = 200; int yresolution = 200; string filename = file_name; params.AddInt("xresolution",&xresolution,1); params.AddInt("yresolution",&yresolution,1); params.AddString("filename",&filename,1); //采樣器 int pixelsample = 4; params.AddInt("pixelsamples",&pixelsample,1); //過濾器 float xwidth = 0.5; float ywidth = 0.5; params.AddFloat("xwith",&xwidth,1); params.AddFloat("ywith",&ywidth,1); //光照 float L[3] = {0.5,0.5,0.5}; int nsamples = 4; string mapname = "textures/skylight-dusk.exr"; params.AddRGBSpectrum("L",L,3); params.AddInt("nsamples",&nsamples,1); params.AddString("mapname",&mapname,1); //材質 float Kd[3] = {0,0.1,0.15}; float Kr[3] = {0.9,0.9,0.9}; float Ks[3] = {0.1,0.1,0.1}; float roughness = 0.9; float index = 1.34; params.AddRGBSpectrum("Kd",Kd,3); params.AddRGBSpectrum("Kr",Kr,3); params.AddRGBSpectrum("Ks",Ks,3); params.AddFloat("roughness",&roughness,1); params.AddFloat("index",&index,1); pbrtSurfaceIntegrator("directlighting",params); float ct[16] = {0.828849,-0.295370,-0.475149,0.000000,-0.559473,-0.437585,-0.703924,0.000000,-0.000000,0.849280,-0.527943,0.000000,0.000000,0.000000,0.000000,1.000000}; pbrtConcatTransform(ct); pbrtTranslate(-4.860000,-7.200000,-5.400000); pbrtCamera("perspective",params); pbrtFilm("image",params); pbrtSampler("lowdiscrepancy",params); pbrtPixelFilter("box",params); pbrtWorldBegin(); pbrtAttributeBegin(); pbrtLightSource("infinite",params); pbrtAttributeEnd(); pbrtAttributeBegin(); pbrtMaterial("uber",params); pbrtTranslate(-80,-80,-3); pbrtScale(0.1,0.1,0.1);
for(int i=0;i<N-1;i++) { for(int j=0;j<N-1;j++) { create_a_tri( Point((j+lamda*pxs[0]->getColor(j,i).r)*LL/N,(i+lamda* pxs[1]->getColor(j,i).r)*LL/N,pxs[2]->getColor(j,i).r*N/2), Point((j+1+lamda*pxs[0]->getColor(j+1,i).r)*LL/N,(i+lamda* pxs[1]->getColor(j+1,i).r)*LL/N,pxs[2]->getColor(j+1,i).r*N/2), Point((j+lamda*pxs[0]->getColor(j,i+1).r)*LL/N,(i+1+lamda* pxs[1]->getColor(j,i+1).r)*LL/N,pxs[2]->getColor(j,i+1).r*N/2), Normal(nla*pxs[3]->getColor(j,i).r,nla*pxs[3]->getColor(j,i).g,nla*pxs[3]->getColor(j,i).b), Normal(nla*pxs[3]->getColor(j+1,i).r,nla*pxs[3]->getColor(j+1,i).g,nla*pxs[3]->getColor(j+1,i).b), Normal(nla*pxs[3]->getColor(j,i+1).r,nla*pxs[3]->getColor(j,i+1).g,nla*pxs[3]->getColor(j,i+1).b) ); create_a_tri( Point((j+1+lamda*pxs[0]->getColor(j+1,i+1).r)*LL/N,(i+1+lamda* pxs[1]->getColor(j+1,1+i).r)*LL/N,pxs[2]->getColor(j+1,i+1).r*N/2), Point((j+1+lamda*pxs[0]->getColor(j+1,i).r)*LL/N,(i+lamda* pxs[1]->getColor(j+1,i).r)*LL/N,pxs[2]->getColor(j+1,i).r*N/2), Point((j+lamda*pxs[0]->getColor(j,i+1).r)*LL/N,(i+1+lamda* pxs[1]->getColor(j,i+1).r)*LL/N,pxs[2]->getColor(j,i+1).r*N/2), Normal(nla*pxs[3]->getColor(j+1,i+1).r,nla*pxs[3]->getColor(j+1,i+1).g,nla*pxs[3]->getColor(j+1,i+1).b), Normal(nla*pxs[3]->getColor(j+1,i).r,nla*pxs[3]->getColor(j+1,i).g,nla*pxs[3]->getColor(j+1,i).b), Normal(nla*pxs[3]->getColor(j,i+1).r,nla*pxs[3]->getColor(j,i+1).g,nla*pxs[3]->getColor(j,i+1).b) ); } } pbrtAttributeEnd(); pbrtWorldEnd(); pbrtCleanup(); return 0; }
雖然這樣效率比較低,但是至少可以脫手了。
每幀我們計算完動畫,自動調用render函數,就可以在無人看守下出圖了。
最后,關於pbrt中關於圖形學的內容就不細說了,東西都寫在pbrt里面,uber材質是一種綜合材質。
放2張圖結尾吧: