clray

annotate src/rt.cc @ 47:30bf84881553

added interactive controls for turning shadows/reflections on and off as well as selecting maximum ray tracing iterations
author John Tsiombikas <nuclear@member.fsf.org>
date Tue, 31 Aug 2010 01:47:27 +0100
parents 8047637961a2
children d3c46803242e
rev   line source
nuclear@0 1 #include <stdio.h>
nuclear@8 2 #include <string.h>
nuclear@2 3 #include <math.h>
nuclear@0 4 #include <assert.h>
nuclear@47 5 #include "rt.h"
John@14 6 #include "ogl.h"
nuclear@0 7 #include "ocl.h"
nuclear@22 8 #include "scene.h"
nuclear@32 9 #include "timer.h"
nuclear@45 10 #include "common.h"
nuclear@0 11
nuclear@12 12 // kernel arguments
nuclear@12 13 enum {
nuclear@12 14 KARG_FRAMEBUFFER,
nuclear@12 15 KARG_RENDER_INFO,
nuclear@12 16 KARG_FACES,
nuclear@12 17 KARG_MATLIB,
nuclear@12 18 KARG_LIGHTS,
nuclear@12 19 KARG_PRIM_RAYS,
nuclear@12 20 KARG_XFORM,
John@14 21 KARG_INVTRANS_XFORM,
nuclear@28 22 KARG_KDTREE,
John@14 23
John@14 24 NUM_KERNEL_ARGS
nuclear@12 25 };
John@11 26
nuclear@2 27 struct RendInfo {
nuclear@22 28 float ambient[4];
nuclear@2 29 int xsz, ysz;
nuclear@9 30 int num_faces, num_lights;
nuclear@2 31 int max_iter;
nuclear@47 32 int cast_shadows;
nuclear@12 33 };
nuclear@2 34
nuclear@1 35 struct Ray {
nuclear@8 36 float origin[4], dir[4];
nuclear@12 37 };
nuclear@1 38
nuclear@3 39 struct Light {
nuclear@8 40 float pos[4], color[4];
nuclear@12 41 };
nuclear@1 42
nuclear@47 43 static void update_render_info();
nuclear@3 44 static Ray get_primary_ray(int x, int y, int w, int h, float vfov_deg);
nuclear@43 45 static float *create_kdimage(const KDNodeGPU *kdtree, int num_nodes, int *xsz_ret, int *ysz_ret);
nuclear@3 46
nuclear@13 47 static Face *faces;
nuclear@3 48 static Ray *prim_rays;
nuclear@3 49 static CLProgram *prog;
nuclear@3 50 static int global_size;
nuclear@3 51
nuclear@4 52 static Light lightlist[] = {
nuclear@22 53 {{-8, 15, 18, 0}, {1, 1, 1, 1}}
nuclear@4 54 };
nuclear@4 55
nuclear@7 56
nuclear@4 57 static RendInfo rinf;
nuclear@47 58 static int saved_iter_val;
nuclear@4 59
nuclear@43 60 static long timing_sample_sum;
nuclear@43 61 static long num_timing_samples;
nuclear@43 62
nuclear@4 63
nuclear@39 64 bool init_renderer(int xsz, int ysz, Scene *scn, unsigned int tex)
nuclear@0 65 {
nuclear@4 66 // render info
nuclear@22 67 rinf.ambient[0] = rinf.ambient[1] = rinf.ambient[2] = 0.0;
nuclear@16 68 rinf.ambient[3] = 0.0;
nuclear@16 69
nuclear@4 70 rinf.xsz = xsz;
nuclear@4 71 rinf.ysz = ysz;
nuclear@13 72 rinf.num_faces = scn->get_num_faces();
nuclear@4 73 rinf.num_lights = sizeof lightlist / sizeof *lightlist;
nuclear@47 74 rinf.max_iter = saved_iter_val = 6;
nuclear@47 75 rinf.cast_shadows = true;
nuclear@4 76
nuclear@3 77 /* calculate primary rays */
nuclear@3 78 prim_rays = new Ray[xsz * ysz];
nuclear@2 79
nuclear@2 80 for(int i=0; i<ysz; i++) {
nuclear@2 81 for(int j=0; j<xsz; j++) {
nuclear@2 82 prim_rays[i * xsz + j] = get_primary_ray(j, i, xsz, ysz, 45.0);
nuclear@2 83 }
nuclear@0 84 }
nuclear@0 85
nuclear@2 86 /* setup opencl */
nuclear@3 87 prog = new CLProgram("render");
nuclear@3 88 if(!prog->load("rt.cl")) {
nuclear@8 89 return false;
nuclear@0 90 }
nuclear@0 91
nuclear@24 92 if(!(faces = (Face*)scn->get_face_buffer())) {
nuclear@13 93 fprintf(stderr, "failed to create face buffer\n");
nuclear@13 94 return false;
nuclear@13 95 }
nuclear@13 96
nuclear@28 97 const KDNodeGPU *kdbuf = scn->get_kdtree_buffer();
nuclear@28 98 if(!kdbuf) {
nuclear@28 99 fprintf(stderr, "failed to create kdtree buffer\n");
nuclear@28 100 return false;
nuclear@28 101 }
nuclear@43 102
nuclear@43 103 int kdimg_xsz, kdimg_ysz;
nuclear@43 104 float *kdimg_pixels = create_kdimage(kdbuf, scn->get_num_kdnodes(), &kdimg_xsz, &kdimg_ysz);
nuclear@28 105
nuclear@3 106 /* setup argument buffers */
nuclear@41 107 #ifdef CLGL_INTEROP
nuclear@39 108 prog->set_arg_texture(KARG_FRAMEBUFFER, ARG_WR, tex);
nuclear@41 109 #else
nuclear@41 110 prog->set_arg_image(KARG_FRAMEBUFFER, ARG_WR, xsz, ysz);
nuclear@41 111 #endif
nuclear@12 112 prog->set_arg_buffer(KARG_RENDER_INFO, ARG_RD, sizeof rinf, &rinf);
John@14 113 prog->set_arg_buffer(KARG_FACES, ARG_RD, rinf.num_faces * sizeof(Face), faces);
John@14 114 prog->set_arg_buffer(KARG_MATLIB, ARG_RD, scn->get_num_materials() * sizeof(Material), scn->get_materials());
nuclear@12 115 prog->set_arg_buffer(KARG_LIGHTS, ARG_RD, sizeof lightlist, lightlist);
nuclear@12 116 prog->set_arg_buffer(KARG_PRIM_RAYS, ARG_RD, xsz * ysz * sizeof *prim_rays, prim_rays);
nuclear@12 117 prog->set_arg_buffer(KARG_XFORM, ARG_RD, 16 * sizeof(float));
nuclear@12 118 prog->set_arg_buffer(KARG_INVTRANS_XFORM, ARG_RD, 16 * sizeof(float));
nuclear@43 119 //prog->set_arg_buffer(KARG_KDTREE, ARG_RD, scn->get_num_kdnodes() * sizeof *kdbuf, kdbuf);
nuclear@43 120 prog->set_arg_image(KARG_KDTREE, ARG_RD, kdimg_xsz, kdimg_ysz, kdimg_pixels);
nuclear@43 121
nuclear@43 122 delete [] kdimg_pixels;
nuclear@43 123
nuclear@12 124
John@14 125 if(prog->get_num_args() < NUM_KERNEL_ARGS) {
John@14 126 return false;
John@14 127 }
John@14 128
nuclear@45 129 const char *opt = "-Isrc -cl-mad-enable -cl-single-precision-constant -cl-fast-relaxed-math";
nuclear@45 130 if(!prog->build(opt)) {
nuclear@16 131 return false;
nuclear@16 132 }
nuclear@16 133
nuclear@12 134 delete [] prim_rays;
nuclear@2 135
nuclear@3 136 global_size = xsz * ysz;
nuclear@3 137 return true;
nuclear@3 138 }
nuclear@3 139
nuclear@3 140 void destroy_renderer()
nuclear@3 141 {
nuclear@3 142 delete prog;
nuclear@43 143
nuclear@43 144 printf("rendertime mean: %ld msec\n", timing_sample_sum / num_timing_samples);
nuclear@3 145 }
nuclear@3 146
nuclear@3 147 bool render()
nuclear@3 148 {
nuclear@39 149 // XXX do we need to call glFinish ?
nuclear@39 150
nuclear@32 151 long tm0 = get_msec();
nuclear@32 152
nuclear@40 153 #ifdef CLGL_INTEROP
nuclear@39 154 cl_event ev;
nuclear@39 155 CLMemBuffer *texbuf = prog->get_arg_buffer(KARG_FRAMEBUFFER);
nuclear@39 156
nuclear@39 157 if(!acquire_gl_object(texbuf, &ev)) {
nuclear@39 158 return false;
nuclear@39 159 }
nuclear@39 160
nuclear@39 161 // make sure that we will wait for the acquire to finish before running
nuclear@39 162 prog->set_wait_event(ev);
nuclear@40 163 #endif
nuclear@39 164
nuclear@3 165 if(!prog->run(1, global_size)) {
nuclear@3 166 return false;
nuclear@0 167 }
John@15 168
nuclear@40 169 #ifdef CLGL_INTEROP
nuclear@39 170 if(!release_gl_object(texbuf, &ev)) {
nuclear@39 171 return false;
nuclear@39 172 }
nuclear@39 173 clWaitForEvents(1, &ev);
nuclear@40 174 #endif
nuclear@39 175
nuclear@40 176 #ifndef CLGL_INTEROP
nuclear@40 177 /* if we don't compile in CL/GL interoperability support, we need
nuclear@40 178 * to copy the output buffer to the OpenGL texture used to displaying
nuclear@40 179 * the image.
nuclear@40 180 */
nuclear@13 181 CLMemBuffer *mbuf = prog->get_arg_buffer(KARG_FRAMEBUFFER);
nuclear@12 182 void *fb = map_mem_buffer(mbuf, MAP_RD);
nuclear@13 183 if(!fb) {
nuclear@13 184 fprintf(stderr, "FAILED\n");
nuclear@13 185 return false;
nuclear@13 186 }
nuclear@13 187
nuclear@12 188 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, rinf.xsz, rinf.ysz, GL_RGBA, GL_FLOAT, fb);
nuclear@2 189 unmap_mem_buffer(mbuf);
nuclear@40 190 #endif
nuclear@32 191
nuclear@43 192 long msec = get_msec() - tm0;
nuclear@43 193 timing_sample_sum += msec;
nuclear@43 194 num_timing_samples++;
nuclear@43 195
nuclear@43 196 printf("rendered in %ld msec\n", msec);
nuclear@3 197 return true;
nuclear@0 198 }
nuclear@2 199
nuclear@27 200 #define MIN(a, b) ((a) < (b) ? (a) : (b))
nuclear@21 201 static void dbg_set_gl_material(Material *mat)
nuclear@21 202 {
nuclear@21 203 static Material def_mat = {{0.7, 0.7, 0.7, 1}, {0, 0, 0, 0}, 0, 0, 0};
nuclear@21 204
nuclear@21 205 if(!mat) mat = &def_mat;
nuclear@21 206
nuclear@21 207 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat->kd);
nuclear@21 208 glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat->ks);
nuclear@27 209 glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, MIN(mat->spow, 128.0f));
nuclear@21 210 }
nuclear@21 211
nuclear@27 212 void dbg_render_gl(Scene *scn, bool show_tree, bool show_obj)
nuclear@8 213 {
nuclear@22 214 glPushAttrib(GL_ENABLE_BIT | GL_TRANSFORM_BIT | GL_LIGHTING_BIT);
nuclear@8 215
nuclear@21 216 for(int i=0; i<rinf.num_lights; i++) {
nuclear@21 217 float lpos[4];
nuclear@21 218
nuclear@21 219 memcpy(lpos, lightlist[i].pos, sizeof lpos);
nuclear@21 220 lpos[3] = 1.0;
nuclear@21 221
nuclear@21 222 glLightfv(GL_LIGHT0 + i, GL_POSITION, lpos);
nuclear@21 223 glLightfv(GL_LIGHT0 + i, GL_DIFFUSE, lightlist[i].color);
nuclear@22 224 glEnable(GL_LIGHT0 + i);
nuclear@21 225 }
nuclear@21 226
nuclear@12 227 glDisable(GL_TEXTURE_2D);
nuclear@12 228 glEnable(GL_DEPTH_TEST);
John@15 229 glEnable(GL_LIGHTING);
nuclear@12 230
nuclear@12 231 glMatrixMode(GL_PROJECTION);
nuclear@12 232 glPushMatrix();
nuclear@12 233 glLoadIdentity();
nuclear@12 234 gluPerspective(45.0, (float)rinf.xsz / (float)rinf.ysz, 0.5, 1000.0);
nuclear@12 235
nuclear@27 236 if(show_obj) {
nuclear@27 237 Material *materials = scn->get_materials();
John@14 238
nuclear@27 239 int num_faces = scn->get_num_faces();
nuclear@27 240 int cur_mat = -1;
nuclear@21 241
nuclear@27 242 for(int i=0; i<num_faces; i++) {
nuclear@27 243 if(faces[i].matid != cur_mat) {
nuclear@27 244 if(cur_mat != -1) {
nuclear@27 245 glEnd();
nuclear@27 246 }
nuclear@27 247 dbg_set_gl_material(materials ? materials + faces[i].matid : 0);
nuclear@27 248 cur_mat = faces[i].matid;
nuclear@27 249 glBegin(GL_TRIANGLES);
nuclear@21 250 }
nuclear@27 251
nuclear@27 252 for(int j=0; j<3; j++) {
nuclear@27 253 glNormal3fv(faces[i].v[j].normal);
nuclear@27 254 glVertex3fv(faces[i].v[j].pos);
nuclear@27 255 }
John@14 256 }
nuclear@27 257 glEnd();
nuclear@27 258 }
nuclear@12 259
nuclear@27 260 if(show_tree) {
nuclear@27 261 scn->draw_kdtree();
nuclear@12 262 }
nuclear@12 263
nuclear@12 264 glPopMatrix();
nuclear@12 265 glPopAttrib();
nuclear@22 266
nuclear@22 267 assert(glGetError() == GL_NO_ERROR);
nuclear@12 268 }
nuclear@12 269
nuclear@12 270 void set_xform(float *matrix, float *invtrans)
nuclear@12 271 {
nuclear@12 272 CLMemBuffer *mbuf_xform = prog->get_arg_buffer(KARG_XFORM);
nuclear@12 273 CLMemBuffer *mbuf_invtrans = prog->get_arg_buffer(KARG_INVTRANS_XFORM);
nuclear@12 274 assert(mbuf_xform && mbuf_invtrans);
nuclear@12 275
nuclear@12 276 float *mem = (float*)map_mem_buffer(mbuf_xform, MAP_WR);
nuclear@12 277 memcpy(mem, matrix, 16 * sizeof *mem);
nuclear@12 278 unmap_mem_buffer(mbuf_xform);
nuclear@12 279
nuclear@12 280 mem = (float*)map_mem_buffer(mbuf_invtrans, MAP_WR);
nuclear@12 281 memcpy(mem, invtrans, 16 * sizeof *mem);
nuclear@12 282 unmap_mem_buffer(mbuf_invtrans);
nuclear@8 283 }
nuclear@8 284
nuclear@47 285 void set_render_option(int opt, bool val)
nuclear@47 286 {
nuclear@47 287 switch(opt) {
nuclear@47 288 case ROPT_ITER:
nuclear@47 289 case ROPT_REFL:
nuclear@47 290 rinf.max_iter = val ? saved_iter_val : 0;
nuclear@47 291 break;
nuclear@47 292
nuclear@47 293 case ROPT_SHAD:
nuclear@47 294 rinf.cast_shadows = val;
nuclear@47 295 break;
nuclear@47 296
nuclear@47 297 default:
nuclear@47 298 return;
nuclear@47 299 }
nuclear@47 300
nuclear@47 301 update_render_info();
nuclear@47 302 }
nuclear@47 303
nuclear@47 304 void set_render_option(int opt, int val)
nuclear@47 305 {
nuclear@47 306 switch(opt) {
nuclear@47 307 case ROPT_ITER:
nuclear@47 308 rinf.max_iter = saved_iter_val = val;
nuclear@47 309 break;
nuclear@47 310
nuclear@47 311 case ROPT_SHAD:
nuclear@47 312 rinf.cast_shadows = val;
nuclear@47 313 break;
nuclear@47 314
nuclear@47 315 case ROPT_REFL:
nuclear@47 316 rinf.max_iter = val ? saved_iter_val : 0;
nuclear@47 317 break;
nuclear@47 318
nuclear@47 319 default:
nuclear@47 320 return;
nuclear@47 321 }
nuclear@47 322
nuclear@47 323 update_render_info();
nuclear@47 324 }
nuclear@47 325
nuclear@47 326 void set_render_option(int opt, float val)
nuclear@47 327 {
nuclear@47 328 set_render_option(opt, (int)val);
nuclear@47 329 }
nuclear@47 330
nuclear@47 331 bool get_render_option_bool(int opt)
nuclear@47 332 {
nuclear@47 333 switch(opt) {
nuclear@47 334 case ROPT_ITER:
nuclear@47 335 return rinf.max_iter;
nuclear@47 336 case ROPT_SHAD:
nuclear@47 337 return rinf.cast_shadows;
nuclear@47 338 case ROPT_REFL:
nuclear@47 339 return rinf.max_iter == saved_iter_val;
nuclear@47 340 default:
nuclear@47 341 break;
nuclear@47 342 }
nuclear@47 343 return false;
nuclear@47 344 }
nuclear@47 345
nuclear@47 346 int get_render_option_int(int opt)
nuclear@47 347 {
nuclear@47 348 switch(opt) {
nuclear@47 349 case ROPT_ITER:
nuclear@47 350 return rinf.max_iter;
nuclear@47 351 case ROPT_SHAD:
nuclear@47 352 return rinf.cast_shadows ? 1 : 0;
nuclear@47 353 case ROPT_REFL:
nuclear@47 354 return rinf.max_iter == saved_iter_val ? 1 : 0;
nuclear@47 355 default:
nuclear@47 356 break;
nuclear@47 357 }
nuclear@47 358 return -1;
nuclear@47 359 }
nuclear@47 360
nuclear@47 361 float get_render_option_float(int opt)
nuclear@47 362 {
nuclear@47 363 return (float)get_render_option_int(opt);
nuclear@47 364 }
nuclear@47 365
nuclear@47 366 static void update_render_info()
nuclear@47 367 {
nuclear@47 368 if(!prog) {
nuclear@47 369 return;
nuclear@47 370 }
nuclear@47 371
nuclear@47 372 CLMemBuffer *mbuf = prog->get_arg_buffer(KARG_RENDER_INFO);
nuclear@47 373 assert(mbuf);
nuclear@47 374
nuclear@47 375 RendInfo *rinf_ptr = (RendInfo*)map_mem_buffer(mbuf, MAP_WR);
nuclear@47 376 *rinf_ptr = rinf;
nuclear@47 377 unmap_mem_buffer(mbuf);
nuclear@47 378 }
nuclear@47 379
nuclear@3 380 static Ray get_primary_ray(int x, int y, int w, int h, float vfov_deg)
nuclear@2 381 {
nuclear@2 382 float vfov = M_PI * vfov_deg / 180.0;
nuclear@2 383 float aspect = (float)w / (float)h;
nuclear@2 384
nuclear@2 385 float ysz = 2.0;
nuclear@2 386 float xsz = aspect * ysz;
nuclear@2 387
nuclear@2 388 float px = ((float)x / (float)w) * xsz - xsz / 2.0;
nuclear@2 389 float py = 1.0 - ((float)y / (float)h) * ysz;
nuclear@2 390 float pz = 1.0 / tan(0.5 * vfov);
nuclear@2 391
nuclear@43 392 float mag = sqrt(px * px + py * py + pz * pz);
nuclear@43 393
nuclear@45 394 px = px * RAY_MAG / mag;
nuclear@45 395 py = py * RAY_MAG / mag;
nuclear@45 396 pz = pz * RAY_MAG / mag;
nuclear@2 397
nuclear@18 398 Ray ray = {{0, 0, 0, 1}, {px, py, -pz, 1}};
nuclear@2 399 return ray;
nuclear@2 400 }
nuclear@43 401
nuclear@43 402 static float *create_kdimage(const KDNodeGPU *kdtree, int num_nodes, int *xsz_ret, int *ysz_ret)
nuclear@43 403 {
nuclear@45 404 int ysz = MIN(num_nodes, KDIMG_MAX_HEIGHT);
nuclear@45 405 int columns = (num_nodes - 1) / KDIMG_MAX_HEIGHT + 1;
nuclear@45 406 int xsz = KDIMG_NODE_WIDTH * columns;
nuclear@43 407
nuclear@43 408 printf("creating kdtree image %dx%d (%d nodes)\n", xsz, ysz, num_nodes);
nuclear@43 409
nuclear@43 410 float *img = new float[4 * xsz * ysz];
nuclear@43 411 memset(img, 0, 4 * xsz * ysz * sizeof *img);
nuclear@43 412
nuclear@43 413 for(int i=0; i<num_nodes; i++) {
nuclear@45 414 int x = KDIMG_NODE_WIDTH * (i / KDIMG_MAX_HEIGHT);
nuclear@45 415 int y = i % KDIMG_MAX_HEIGHT;
nuclear@45 416
nuclear@45 417 float *ptr = img + (y * xsz + x) * 4;
nuclear@43 418
nuclear@43 419 *ptr++ = kdtree[i].aabb.min[0];
nuclear@43 420 *ptr++ = kdtree[i].aabb.min[1];
nuclear@43 421 *ptr++ = kdtree[i].aabb.min[2];
nuclear@43 422 *ptr++ = 0.0;
nuclear@43 423
nuclear@43 424 *ptr++ = kdtree[i].aabb.max[0];
nuclear@43 425 *ptr++ = kdtree[i].aabb.max[1];
nuclear@43 426 *ptr++ = kdtree[i].aabb.max[2];
nuclear@43 427 *ptr++ = 0.0;
nuclear@43 428
nuclear@43 429 for(int j=0; j<MAX_NODE_FACES; j++) {
nuclear@43 430 *ptr++ = j < kdtree[i].num_faces ? (float)kdtree[i].face_idx[j] : 0.0f;
nuclear@43 431 }
nuclear@43 432
nuclear@43 433 *ptr++ = (float)kdtree[i].num_faces;
nuclear@43 434 *ptr++ = (float)kdtree[i].left;
nuclear@43 435 *ptr++ = (float)kdtree[i].right;
nuclear@43 436 *ptr++ = 0.0;
nuclear@43 437 }
nuclear@43 438
nuclear@43 439 if(xsz_ret) *xsz_ret = xsz;
nuclear@43 440 if(ysz_ret) *ysz_ret = ysz;
nuclear@43 441 return img;
nuclear@43 442 }