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