View RSS Feed

JarkkoL

Implementing Multi-Threaded Ray-Tracer

Rate this Entry
by , 08-06-2011 at 04:15 PM (7411 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
Tags: None Add / Edit Tags
Categories
Uncategorized

Comments

kas ekimi sac ekimi