Implementing Multi-Threaded Ray-Tracer
by
, 08-06-2011 at 04:15 PM (1479 Views)
If you are interested in ray-tracer implementation basics or like to know how to multi-thread code using Spin-X Platform, here's a video that you may find useful:
After reviewing the code I realized there was a small bug in the shadow calculation: The shadow ray intersection must check that the ray-object intersection is on the positive side of the ray (i.e. imint>0.0f). Otherwise objects that are on the shadow line but further from the light than the primary ray intersection point will also shadow the point (scene looks over-shadowed). This is fixed in the code below. I also added a small fix to avoid potential issues that the reflected ray might re-intersect with the object it just reflected off.
Code cpp:#include "core/main.h" #include "core/math/tform3.h" #include "core/math/color.h" #include "core/math/prim3_isect.h" #include "core/mp/mp_job_queue.h" #include "core/fsys/fsys.h" using namespace pfc; //---------------------------------------------------------------------------- //============================================================================ // config //============================================================================ enum {image_width=1024, image_height=1024, image_block_size=32}; // 32x32px block //---------------------------------------------------------------------------- //============================================================================ // calculate_image_block //============================================================================ struct object { sphere3f sphere; color_rgbaf color; }; //---- struct scene { camera cam; vec3f light_dir; color_rgbaf light_col; color_rgbaf ambient_col; array<object> objects; }; //---- struct image_block { unsigned x, y; uint32 *data; }; //---- void calculate_image_block(const image_block *block_, const scene *scene_) { // render image block const object *objects=scene_->objects.data(); unsigned num_objects=scene_->objects.size(); for(unsigned y=0; y<image_block_size; ++y) for(unsigned x=0; x<image_block_size; ++x) { // setup the ray float sx=float(x+block_->x)*2.0f/image_width-1.0f; float sy=float(y+block_->y)*2.0f/image_height-1.0f; line3f ray=calculate_world_ray(scene_->cam, sx, sy); // trace the ray enum {max_bounches=10}; const object *cur_object=0; const float reflectivity=0.5f; float refl_factor=1.0f-reflectivity; color_rgbaf col(0.0f, 0.0f, 0.0f); for(unsigned ri=0; ri<max_bounches; ++ri) { // get closest sphere the ray intersects float isect_min=numeric_type<float>::range_max; const object *isect_obj=0; for(unsigned i=0; i<num_objects; ++i) { float imint, imaxt; if(cur_object!=objects+i && isect(ray, objects[i].sphere, imint, imaxt) && imint>0.0f && imint<isect_min) { isect_min=imint; isect_obj=objects+i; } } if(!isect_obj) break; // calculate shadow color_rgbaf lcol=scene_->light_col; vec3f p=ray.pos+ray.dir*isect_min; line3f shadow_ray(p, -scene_->light_dir); float imint, imaxt; for(unsigned i=0; i<num_objects; ++i) if(isect_obj!=objects+i && isect(shadow_ray, objects[i].sphere, imint, imaxt) && imint>0.0f) { lcol.set(0.0f, 0.0f, 0.0f); break; } // calculate lighting vec3f normal=unit(p-isect_obj->sphere.pos); col+=(sat(-dot(normal, scene_->light_dir))*lcol+scene_->ambient_col)*isect_obj->color*refl_factor; // setup reflected ray & reflection factor ray.pos=p; ray.dir=reflect_u(-ray.dir, normal); refl_factor*=reflectivity; cur_object=isect_obj; } col.a=1.0f; block_->data[x+y*image_width]=to_u32_argb(sat(col)); } } //---------------------------------------------------------------------------- //============================================================================ // main //============================================================================ PFC_MAIN(const char *args_[], unsigned num_args_) { // setup job queue mp_job_queue jq; // setup scene init_working_dir(); enum {num_objects=100}; scene sc; sc.cam.set_transform(perspective_matrix(60.0f*mathf::deg_to_rad, 1.0f, 0.1f, 100.0f), tform3f(vec3f(0.0f, 0.0f, -10.0f)), 0.1f, 100.0f); sc.light_dir=unit(vec3f(1.0f, -1.0f, 1.0f)); sc.light_col.set(1.0f, 1.0f, 1.0f); sc.ambient_col.set(0.1f, 0.1f, 0.2f); sc.objects.resize(num_objects); randomizer rnd(1); for(unsigned i=0; i<num_objects; ++i) { object &obj=sc.objects[i]; rand_real1(obj.sphere.pos, rnd); obj.sphere.pos*=5.0f; obj.sphere.rad=rnd.rand_ureal1()*0.5f+0.5f; rand_ureal1(obj.color, rnd); } // ray-trace the image owner_data<uint32> image_data=(uint32*)PFC_MEM_ALLOC(image_width*image_height*4); e_jobtype_id jid=create_job_type("raytracing job", calculate_image_block); set_job_type_data(jid, &sc); unsigned bwidth=image_width/image_block_size; unsigned bheight=image_height/image_block_size; array<image_block> image_blocks(bwidth*bheight); timer t; t.start(); unsigned bi=0; for(unsigned by=0; by<bheight; ++by) for(unsigned bx=0; bx<bwidth; ++bx) { // ray-trace the block // image_block block; image_block &block=image_blocks[bi++]; block.x=bx*image_block_size; block.y=by*image_block_size; block.data=image_data.data+block.x+block.y*image_width; add_job(jid, &block); // calculate_image_block(&block, &sc); } wait_job(jid); t.update(); t.stop(); logf("Ray-tracing time: %.3fs\r\n", t.time()); // save TGA file owner_ref<file_system_base> fs=create_default_file_system(); owner_ptr<bin_output_stream_base> ofile=fs->open_write("raytraced.tga"); uint8 header[18]={0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, uint8(image_width&0xff), uint8(image_width>>8), uint8(image_height&0xff), uint8(image_height>>8), 0x20, 0x08}; ofile->write_bytes(header, sizeof(header)); ofile->write_bytes(image_data.data, image_width*image_height*4); return 0; } //----------------------------------------------------------------------------
Cheers, Jarkko





Email Blog Entry