clray

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