erebus
changeset 32:b1fc96c71bcc
- lambert BRDF importance sampling
- UI + commandline arguments
- font rendering for showing status/progress
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Sat, 07 Jun 2014 13:36:36 +0300 |
parents | 53a98c148bf8 |
children | 2c768a49e86e |
files | data/serif.glyphmap erebus.vcxproj liberebus/src/brdf.cc liberebus/src/erebus.cc liberebus/src/erebus.h liberebus/src/threadpool.cc src/main.cc |
diffstat | 7 files changed, 257 insertions(+), 29 deletions(-) [+] |
line diff
1.1 Binary file data/serif.glyphmap has changed
2.1 --- a/erebus.vcxproj Sat Jun 07 09:14:17 2014 +0300 2.2 +++ b/erebus.vcxproj Sat Jun 07 13:36:36 2014 +0300 2.3 @@ -100,7 +100,7 @@ 2.4 <Link> 2.5 <SubSystem>Console</SubSystem> 2.6 <GenerateDebugInformation>true</GenerateDebugInformation> 2.7 - <AdditionalDependencies>opengl32.lib;glut32.lib;glew32.lib;libvmath.lib;libimago2.lib;jpeglib.lib;libpng.lib;zlib.lib;liberebus.lib;%(AdditionalDependencies)</AdditionalDependencies> 2.8 + <AdditionalDependencies>opengl32.lib;glut32.lib;glew32.lib;libvmath.lib;libimago2.lib;jpeglib.lib;libpng.lib;zlib.lib;liberebus.lib;libdrawtext-dbg.lib;%(AdditionalDependencies)</AdditionalDependencies> 2.9 <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> 2.10 </Link> 2.11 </ItemDefinitionGroup> 2.12 @@ -136,7 +136,7 @@ 2.13 <GenerateDebugInformation>true</GenerateDebugInformation> 2.14 <EnableCOMDATFolding>true</EnableCOMDATFolding> 2.15 <OptimizeReferences>true</OptimizeReferences> 2.16 - <AdditionalDependencies>opengl32.lib;glut32.lib;glew32.lib;libvmath.lib;libimago2.lib;jpeglib.lib;libpng.lib;zlib.lib;liberebus.lib;%(AdditionalDependencies)</AdditionalDependencies> 2.17 + <AdditionalDependencies>opengl32.lib;glut32.lib;glew32.lib;libvmath.lib;libimago2.lib;jpeglib.lib;libpng.lib;zlib.lib;liberebus.lib;libdrawtext.lib;%(AdditionalDependencies)</AdditionalDependencies> 2.18 <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> 2.19 </Link> 2.20 </ItemDefinitionGroup>
3.1 --- a/liberebus/src/brdf.cc Sat Jun 07 09:14:17 2014 +0300 3.2 +++ b/liberebus/src/brdf.cc Sat Jun 07 13:36:36 2014 +0300 3.3 @@ -18,7 +18,7 @@ 3.4 } 3.5 3.6 tangent = Vector3(1.0f, 0.0f, 0.0f); 3.7 - if(fabs(dot_product(normal, tangent)) - 1.0f < 1e-4f) { 3.8 + if(fabs(fabs(dot_product(normal, tangent)) - 1.0f) < 1e-4f) { 3.9 tangent = Vector3(0.0f, 0.0f, 1.0f); 3.10 } 3.11 Vector3 bitan = cross_product(normal, tangent); 3.12 @@ -187,8 +187,14 @@ 3.13 // --- class LambertRefl --- 3.14 Vector3 LambertRefl::sample_dir(const SurfaceGeometry &geom, const Vector3 &outdir) const 3.15 { 3.16 - Vector3 dir = Vector3{randf(-1, 1), randf(-1, 1), randf(-1, 1)}.normalized(); 3.17 - return dot_product(dir, geom.normal) < 0.0 ? -dir : dir; 3.18 + // TODO: write a better uniform disk sampling that doesn't rely in rejection 3.19 + Vector3 dir; 3.20 + do { 3.21 + dir = Vector3(randf(-1, 1), randf(-1, 1), 0); 3.22 + } while(dir.length_sq() > 1.0); 3.23 + dir.z = sqrt(1.0 - (dir.x * dir.x + dir.y * dir.y)); 3.24 + 3.25 + return geom.sample_to_world(dir); 3.26 } 3.27 3.28 float LambertRefl::eval(const SurfaceGeometry &geom, const Vector3 &outdir, const Vector3 &indir) const
4.1 --- a/liberebus/src/erebus.cc Sat Jun 07 09:14:17 2014 +0300 4.2 +++ b/liberebus/src/erebus.cc Sat Jun 07 13:36:36 2014 +0300 4.3 @@ -25,10 +25,8 @@ 4.4 struct erebus *ctx = 0; 4.5 try { 4.6 ctx = new struct erebus; 4.7 - ctx->tpool = new ThreadPool; 4.8 } 4.9 catch(...) { 4.10 - delete ctx; 4.11 return 0; 4.12 } 4.13 4.14 @@ -37,6 +35,7 @@ 4.15 ctx->scn = 0; 4.16 ctx->cur_time = 0; 4.17 ctx->cur_frame = 0; 4.18 + ctx->tpool = 0; 4.19 4.20 erb_setoptf(ctx, ERB_OPT_GAMMA, 2.2); 4.21 erb_setopti(ctx, ERB_OPT_MAX_ITER, 6); 4.22 @@ -130,7 +129,11 @@ 4.23 4.24 void erb_begin_frame(struct erebus *ctx, long ms) 4.25 { 4.26 - printf("starting new frame...\n"); 4.27 + if(!ctx->tpool) { 4.28 + int num_threads = erb_getopti(ctx, ERB_OPT_NUM_THREADS); 4.29 + ctx->tpool = new ThreadPool(num_threads); 4.30 + } 4.31 + 4.32 ++ctx->cur_frame; 4.33 ctx->cur_sample = 0; 4.34 ctx->cur_time = ms; 4.35 @@ -201,7 +204,39 @@ 4.36 4.37 int erb_get_progress(struct erebus *ctx) 4.38 { 4.39 - return 0; // TODO 4.40 + struct erb_render_status st; 4.41 + if(erb_get_status(ctx, &st) == -1) { 4.42 + return 0; 4.43 + } 4.44 + return st.progress_percent; 4.45 +} 4.46 + 4.47 +int erb_get_status(struct erebus *ctx, struct erb_render_status *stat) 4.48 +{ 4.49 + long pending = ctx->tpool->pending(); 4.50 + if(!pending) { 4.51 + return -1; 4.52 + } 4.53 + int xsz = ctx->fbimg.get_width(); 4.54 + int ysz = ctx->fbimg.get_height(); 4.55 + int xblocks = (xsz + BLKSZ - 1) / BLKSZ; 4.56 + int yblocks = (ysz + BLKSZ - 1) / BLKSZ; 4.57 + long num_blocks = xblocks * yblocks; 4.58 + 4.59 + stat->frames = stat->max_frames = 0; // TODO 4.60 + 4.61 + stat->blocks = num_blocks - pending; 4.62 + stat->max_blocks = num_blocks; 4.63 + 4.64 + stat->samples = ctx->cur_sample ? ctx->cur_sample - 1 : 0; 4.65 + if((stat->max_samples = erb_getopti(ctx, ERB_OPT_MAX_SAMPLES)) == INF_SAMPLES) { 4.66 + stat->max_samples = stat->samples; 4.67 + 4.68 + stat->progress_percent = 100 * stat->blocks / stat->max_blocks; 4.69 + } else { 4.70 + stat->progress_percent = 100 * stat->samples / stat->max_samples; 4.71 + } 4.72 + return 0; 4.73 } 4.74 4.75 int erb_load_scene(struct erebus *ctx, const char *fname)
5.1 --- a/liberebus/src/erebus.h Sat Jun 07 09:14:17 2014 +0300 5.2 +++ b/liberebus/src/erebus.h Sat Jun 07 13:36:36 2014 +0300 5.3 @@ -14,6 +14,13 @@ 5.4 ERB_NUM_OPTIONS 5.5 }; 5.6 5.7 +struct erb_render_status { 5.8 + long progress_percent; // derived from the data below 5.9 + long blocks, max_blocks; // completed pixel blocks in current sample pass 5.10 + long samples, max_samples; // completed samples in current frame 5.11 + long frames, max_frames; // completed frames in multi-frame job 5.12 +}; 5.13 + 5.14 #ifdef __cplusplus 5.15 extern "C" { 5.16 #endif 5.17 @@ -36,6 +43,7 @@ 5.18 int erb_render_rect(struct erebus *ctx, int x, int y, int width, int height, long timeout); 5.19 5.20 int erb_get_progress(struct erebus *ctx); 5.21 +int erb_get_status(struct erebus *ctx, struct erb_render_status *stat); 5.22 5.23 int erb_load_scene(struct erebus *ctx, const char *fname); 5.24
6.1 --- a/liberebus/src/threadpool.cc Sat Jun 07 09:14:17 2014 +0300 6.2 +++ b/liberebus/src/threadpool.cc Sat Jun 07 13:36:36 2014 +0300 6.3 @@ -50,7 +50,6 @@ 6.4 putchar('.'); 6.5 fflush(stdout); 6.6 } 6.7 - 6.8 #else 6.9 // spin until all threads are done... 6.10 std::unique_lock<std::mutex> lock(workq_mutex);
7.1 --- a/src/main.cc Sat Jun 07 09:14:17 2014 +0300 7.2 +++ b/src/main.cc Sat Jun 07 13:36:36 2014 +0300 7.3 @@ -5,19 +5,21 @@ 7.4 #include <vector> 7.5 #include <chrono> 7.6 #include <imago2.h> 7.7 +#include <drawtext.h> 7.8 #include "opengl.h" 7.9 #include "erebus.h" 7.10 7.11 using namespace std::chrono; 7.12 7.13 -#define SCALE 2 7.14 - 7.15 static bool init(); 7.16 static void cleanup(); 7.17 +static void begin_frame(long tm); 7.18 +static void end_frame(); 7.19 static void resize_rtarget(int xsz, int ysz); 7.20 static void update_rect(int x, int y, int xsz, int ysz, float *pixels); 7.21 static void idle(); 7.22 static void display(); 7.23 +static void display_statusbar(const erb_render_status &status); 7.24 static void save_image(const char *fname = 0); 7.25 static void reshape(int x, int y); 7.26 static void keyb(unsigned char key, int x, int y); 7.27 @@ -28,22 +30,32 @@ 7.28 static void sball_motion(int x, int y, int z); 7.29 static int next_pow2(int x); 7.30 static void sighandler(int s); 7.31 +static bool parse_args(int argc, char **argv); 7.32 7.33 -static int width, height, rtex_width, rtex_height; 7.34 +static int win_width, win_height, width, height, rtex_width, rtex_height; 7.35 static unsigned int rtex; 7.36 7.37 +static int opt_samples = -1; 7.38 +static int opt_iter = -1; 7.39 +static int opt_threads = -1; 7.40 +static float opt_imgscale = 2.0f; 7.41 + 7.42 static erebus *erb; 7.43 static bool render_pending; 7.44 +static bool show_status = true; 7.45 +static steady_clock::time_point start_time; 7.46 7.47 static std::vector<char*> sfiles; 7.48 7.49 +static dtx_font *font; 7.50 + 7.51 int main(int argc, char **argv) 7.52 { 7.53 glutInitWindowSize(1024, 600); 7.54 glutInit(&argc, argv); 7.55 7.56 - for(int i=1; i<argc; i++) { 7.57 - sfiles.push_back(argv[i]); 7.58 + if(!parse_args(argc, argv)) { 7.59 + return 1; 7.60 } 7.61 7.62 glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE); 7.63 @@ -73,8 +85,13 @@ 7.64 7.65 static bool init() 7.66 { 7.67 - width = glutGet(GLUT_WINDOW_WIDTH) / SCALE; 7.68 - height = glutGet(GLUT_WINDOW_HEIGHT) / SCALE; 7.69 + width = glutGet(GLUT_WINDOW_WIDTH) / opt_imgscale; 7.70 + height = glutGet(GLUT_WINDOW_HEIGHT) / opt_imgscale; 7.71 + 7.72 + if(!(font = dtx_open_font_glyphmap("data/serif.glyphmap"))) { 7.73 + fprintf(stderr, "warning: failed to load font!\n"); 7.74 + } 7.75 + dtx_use_font(font, 24); 7.76 7.77 if(!(erb = erb_init())) { 7.78 return false; 7.79 @@ -82,6 +99,16 @@ 7.80 erb_setopti(erb, ERB_OPT_WIDTH, width); 7.81 erb_setopti(erb, ERB_OPT_HEIGHT, height); 7.82 7.83 + if(opt_samples != -1) { 7.84 + erb_setopti(erb, ERB_OPT_MAX_SAMPLES, opt_samples); 7.85 + } 7.86 + if(opt_iter != -1) { 7.87 + erb_setopti(erb, ERB_OPT_MAX_ITER, opt_iter); 7.88 + } 7.89 + if(opt_threads != -1) { 7.90 + erb_setopti(erb, ERB_OPT_NUM_THREADS, opt_threads); 7.91 + } 7.92 + 7.93 for(size_t i=0; i<sfiles.size(); i++) { 7.94 printf("loading scene file: %s\n", sfiles[i]); 7.95 if(erb_load_scene(erb, sfiles[i]) == -1) { 7.96 @@ -90,10 +117,7 @@ 7.97 } 7.98 7.99 if(!sfiles.empty()) { 7.100 - printf("begin rendering\n"); 7.101 - render_pending = true; 7.102 - glutIdleFunc(idle); 7.103 - erb_begin_frame(erb, 0); 7.104 + begin_frame(0); 7.105 } 7.106 7.107 glEnable(GL_TEXTURE_2D); 7.108 @@ -106,12 +130,59 @@ 7.109 erb_destroy(erb); 7.110 } 7.111 7.112 +static void begin_frame(long tm) 7.113 +{ 7.114 + printf("rendering frame (t=%ld) ... ", tm); 7.115 + fflush(stdout); 7.116 + 7.117 + render_pending = true; 7.118 + glutIdleFunc(idle); 7.119 + erb_begin_frame(erb, 0); 7.120 + 7.121 + start_time = steady_clock::now(); 7.122 +} 7.123 + 7.124 +static void end_frame() 7.125 +{ 7.126 + if(!render_pending) return; 7.127 + 7.128 + auto dur = steady_clock::now() - start_time; 7.129 + long full_msec = duration_cast<milliseconds>(dur).count(); 7.130 + long msec, sec, min, hr, days; 7.131 + 7.132 + msec = full_msec; 7.133 + printf("done in "); 7.134 + if((sec = msec / 1000) > 0) { 7.135 + msec %= 1000; 7.136 + if((min = sec / 60) > 0) { 7.137 + sec %= 60; 7.138 + if((hr = min / 60) > 0) { 7.139 + min %= 60; 7.140 + if((days = hr / 24) > 0) { 7.141 + hr %= 24; 7.142 + printf("%ld days ", days); 7.143 + } 7.144 + printf("%ld hours ", hr); 7.145 + } 7.146 + printf("%ld min ", min); 7.147 + } 7.148 + printf("%ld sec ", sec); 7.149 + } 7.150 + printf("%ld ms (%ld total msec)\n", msec, full_msec); 7.151 + 7.152 + render_pending = false; 7.153 + glutIdleFunc(0); 7.154 +} 7.155 + 7.156 static void resize_rtarget(int xsz, int ysz) 7.157 { 7.158 static unsigned char *defpix; 7.159 7.160 - width = xsz / SCALE; 7.161 - height = ysz / SCALE; 7.162 + win_width = xsz; 7.163 + win_height = ysz; 7.164 + 7.165 + width = xsz / opt_imgscale; 7.166 + height = ysz / opt_imgscale; 7.167 7.168 if(width <= rtex_width && height <= rtex_height) { 7.169 return; 7.170 @@ -160,28 +231,90 @@ 7.171 7.172 static void display() 7.173 { 7.174 + static struct erb_render_status status; 7.175 + 7.176 if(render_pending) { 7.177 if(erb_render(erb, 64) == 0) { 7.178 - render_pending = false; 7.179 - glutIdleFunc(0); 7.180 + end_frame(); 7.181 } 7.182 update_rect(0, 0, width, height, erb_get_framebuffer(erb)); 7.183 + erb_get_status(erb, &status); 7.184 } 7.185 7.186 float maxu = (float)width / (float)rtex_width; 7.187 float maxv = (float)height / (float)rtex_height; 7.188 7.189 glBegin(GL_QUADS); 7.190 + glColor4f(1, 1, 1, 1); 7.191 glTexCoord2f(0, maxv); glVertex2f(-1, -1); 7.192 glTexCoord2f(maxu, maxv); glVertex2f(1, -1); 7.193 glTexCoord2f(maxu, 0); glVertex2f(1, 1); 7.194 glTexCoord2f(0, 0); glVertex2f(-1, 1); 7.195 glEnd(); 7.196 7.197 + // draw progress information etc... 7.198 + if(show_status) { 7.199 + display_statusbar(status); 7.200 + } 7.201 + 7.202 glutSwapBuffers(); 7.203 assert(glGetError() == GL_NO_ERROR); 7.204 } 7.205 7.206 +static void display_statusbar(const erb_render_status &status) 7.207 +{ 7.208 + if(!font) return; 7.209 + 7.210 + bool show_progress = opt_samples > 0; 7.211 + 7.212 + glPushAttrib(GL_ENABLE_BIT); 7.213 + glDisable(GL_TEXTURE_2D); 7.214 + 7.215 + glMatrixMode(GL_PROJECTION); 7.216 + glPushMatrix(); 7.217 + glLoadIdentity(); 7.218 + glOrtho(0, win_width, 0, win_height, -1, 1); 7.219 + 7.220 + glMatrixMode(GL_MODELVIEW); 7.221 + glPushMatrix(); 7.222 + glLoadIdentity(); 7.223 + 7.224 + int font_height = dtx_glyph_height('Q'); 7.225 + int prog_width = show_progress ? status.progress_percent * win_width / 100 : 0; 7.226 + 7.227 + glBegin(GL_QUADS); 7.228 + glColor4f(0, 0, 0, 1); 7.229 + glVertex2f(prog_width, 0); 7.230 + glVertex2f(win_width, 0); 7.231 + glVertex2f(win_width, font_height); 7.232 + glVertex2f(prog_width, font_height); 7.233 + 7.234 + glColor4f(0.25, 0, 0, 1); 7.235 + glVertex2f(0, 0); 7.236 + glVertex2f(prog_width, 0); 7.237 + glVertex2f(prog_width, font_height); 7.238 + glVertex2f(0, font_height); 7.239 + glEnd(); 7.240 + 7.241 + glTranslatef(5, 5, 0); 7.242 + 7.243 + glColor4f(1, 1, 1, 1); 7.244 + 7.245 + if(opt_samples > 0) { 7.246 + dtx_printf("samples: %d / %d\n", status.samples, status.max_samples); 7.247 + 7.248 + glTranslatef(win_width - dtx_string_width("progress: 100%") - 5, 0, 0); 7.249 + dtx_printf("progress: %d%%\n", status.progress_percent); 7.250 + } else { 7.251 + dtx_printf("samples: %d\n", status.samples); 7.252 + } 7.253 + glPopMatrix(); 7.254 + glMatrixMode(GL_PROJECTION); 7.255 + glPopMatrix(); 7.256 + 7.257 + glPopAttrib(); 7.258 +} 7.259 + 7.260 static void save_image(const char *fname) 7.261 { 7.262 float *fb = erb_get_framebuffer(erb); 7.263 @@ -204,19 +337,22 @@ 7.264 { 7.265 switch(key) { 7.266 case 27: 7.267 + end_frame(); 7.268 exit(0); 7.269 7.270 case ' ': 7.271 - printf("begin rendering\n"); 7.272 - render_pending = true; 7.273 - glutIdleFunc(idle); 7.274 - erb_begin_frame(erb, 0); 7.275 + begin_frame(0); 7.276 break; 7.277 7.278 case '`': 7.279 printf("saving image.\n"); 7.280 save_image(); 7.281 break; 7.282 + 7.283 + case 'p': 7.284 + show_status = !show_status; 7.285 + glutPostRedisplay(); 7.286 + break; 7.287 } 7.288 7.289 if(erb_input_keyboard(erb, key, true)) { 7.290 @@ -272,3 +408,47 @@ 7.291 { 7.292 exit(0); 7.293 } 7.294 + 7.295 +static bool parse_args(int argc, char **argv) 7.296 +{ 7.297 + for(int i=1; i<argc; i++) { 7.298 + if(argv[i][0] == '-') { 7.299 + if(strcmp(argv[i], "-samples") == 0) { 7.300 + opt_samples = atoi(argv[++i]); 7.301 + if(opt_samples <= 0) { 7.302 + fprintf(stderr, "invalid -samples option: %s\n", argv[i]); 7.303 + return false; 7.304 + } 7.305 + 7.306 + } else if(strcmp(argv[i], "-iter") == 0) { 7.307 + opt_iter = atoi(argv[++i]); 7.308 + if(opt_iter <= 0) { 7.309 + fprintf(stderr, "invalid -iter option: %s\n", argv[i]); 7.310 + return false; 7.311 + } 7.312 + 7.313 + } else if(strcmp(argv[i], "-threads") == 0) { 7.314 + opt_threads = atoi(argv[++i]); 7.315 + if(opt_threads <= 0) { 7.316 + fprintf(stderr, "invalid -threads option: %s\n", argv[i]); 7.317 + return false; 7.318 + } 7.319 + 7.320 + } else if(strcmp(argv[i], "-scale") == 0) { 7.321 + opt_imgscale = atof(argv[++i]); 7.322 + if(opt_imgscale <= 0.0f) { 7.323 + fprintf(stderr, "invalid -scale option: %s\n", argv[i]); 7.324 + return false; 7.325 + } 7.326 + 7.327 + } else { 7.328 + fprintf(stderr, "invalid option: %s\n", argv[i]); 7.329 + return false; 7.330 + } 7.331 + } else { 7.332 + sfiles.push_back(argv[i]); 7.333 + } 7.334 + } 7.335 + 7.336 + return true; 7.337 +} 7.338 \ No newline at end of file