/RayTracing

Ray Tracer, Path Tracer, multithreading, clustering, live interface

Primary LanguageC

Ray Tracer

Ray Tracer is the last 42 project of the Graphic Branch. I realized this project with nsampre, tdelmas and mmartins.

Let's gear up! Following the RTv1 project, RT takes raytracing to a new level adding the mirror effect, transparency, object cutting, textures and more! Capitalize on your RTv1 project to create an infrastructure capable of managing all the ramifications of new features. Render outstanding computer-generated imagery sure to impress! Team up with your peers and practice the unavoidable future of working with others!

Installation

$ git clone http://github.com/tonoli/RayTracing && cd ./RayTracing && make

/!\ By lazyness kindness we already compiled the SDL2, SDL_TTF and SDL_IMAGE frameworks in common/frameworks. Feel free to update them directly at www.libsdl.org . Oh by the way, this product was only tested for OSX users.

Launch and first steps

This project is client-server oriented. However, you can launch the server directly with the embeded client. To do so :

$ ./rt_server -p 1337

The server will be listening on port 1337, the client will be automatically launched and a 'random' map will be generated. If you a using a nice and old macbook air and you don't want your computer to burn, you might want to launch the server only and launch the server on your brand new $10K iMac Pro 18-cores.

$ ./rt_server -p 1337 --not-local

The server will hang until there is at least one client connected. To launch the client on your brand new $10K iMac Pro, then just execute :

$ ./rt_client <your macbook air address> <1337 or the port you've defined>

Nice, but it's the $10K iMac Pro's right to show what it's computing live !

$ ./rt_client <your macbook air address> <1337 or the port you've defined> --win to show a nice little live computed frame to verify everything justs works fine.

Let's just go back to this command :

./rt_server -p 1337

If everything works fine, you should see this :

And the '1 client connected', meaning rt_server successfully forked and launched the rt_client on background. If not... make a 🎫.

Press a key, and TADAAAAM !

What ? Not sexy ? Blurry ? Weird ? It's the live edition mode. Just press enter or click on the 🎥 on the top right and wait a few seconds.

The first frame will always be noisy. Wait for the next frames to be added and averaged and it'll look smooth. If you like your image, you can save it by clicking on the save button [11]. It will save the image on a .png format in ./images/ and the current scene description on an .xml format in ./scenes/.

Not sexy enought ? OK. I get it. Let's make your own scene then.

Live Scene Editor

First of all let's start from scratch with something empty.

./rt_server -p 1337 ./scenes/void.xml

Let's add a sphere, click on the sphere[A] button

The right menu popped, describing the selected object. To select an object, just click on one.

Just click on a field and modify it with arrow keys.

Now, time for you to get some fun.

Diving into code

Let's talk code. I'll assume you know every bases of Ray Tracing from RTv1.

Now, I am gonna dig into the heart of the path tracing technique.

  • Recursion
t_vector	compute_objects(..., t_ray RAY, ...)
{
	...

	/*
	** Let's get the closest object
	*/

	CURRENT_OBJECT = NULL;
	while (OBJECT_NODE)
	{
		...
		dist = get_distance(OBJECT_NODE, RAY);
		if (dist > 0.0)
		{	
			DIST_MAX = dist;
			CURRENT_OBJECT = OBJECT_NODE;
		}
		OBJECT_NODE = OBJECT_NODE->next;
	}

	/*
	** Assuming we hit an object,
	** let's get a ray that bounces on it
	*/	
	
	if (CURRENT_OBJECT)
		return (bounce_ray(..., CURRENT_OBJECT, RAY));
	...
}
t_vector	bounce_ray(..., t_obj *CURRENT_OBJECT, t_ray RAY)
{
	...

	/*
	** How does the ray bounces on it ?
	** It depends on the material.
	*/

	if (CURRENT_OBJECT->reflection > 0)
	{
		return (reflection(..., RAY, CURRENT_OBJECT));
	}
	else if (CURRENT_OBJECT->refraction > 0)
	{
		return (refraction(..., RAY, CURRENT_OBJECT));
	}
	else
	{
		return (diffuse(..., RAY, CURRENT_OBJECT));
	}
}
  • Diffuse
t_vector	diffuse(..., t_ray ray, t_obj *CURRENT_OBJECT)
{
	...

	/*
	** The light end the recursion
	*/
	
	if (CURRENT_OBJECT->emit_light)
		return (CURRENT_OBJECT->color * CURRENT_OBJECT->light);

	/*
	** The ray bounces on the object and is randomly a little bit deviated
	*/
	
	v = CURRENT_OBJECT->point + CURRENT_OBJECT->normal + random_unit_sphere();

	/*
	** From this point a new ray is created
	*/

	NEW_RAY.ori = CURRENT_OBJECT->hit_point;
	NEW_RAY.dir = v - CURRENT_OBJECT->hit_point;

	/*
	** Let's launch it
	*/

	...

	NEXT_OBJECT_COLOR = compute_objects(..., NEW_RAY, ...);

	NEXT_OBJECT_COLOR.red   *= CURRENT_OBJECT_COLOR.red   * BOUNCE_ABSORPTION;
	NEXT_OBJECT_COLOR.green *= CURRENT_OBJECT_COLOR.green * BOUNCE_ABSORPTION;
	NEXT_OBJECT_COLOR.blue  *= CURRENT_OBJECT_COLOR.blue  * BOUNCE_ABSORPTION;
	
	...

	return (NEXT_OBJECT_COLOR);
}
  • Reflection
t_vector	reflection(..., t_ray RAY, t_obj *CURRENT_OBJECT)
{
	...

	NEW_RAY.dir = RAY.dir - 2.0 * CURRENT_OBJECT->normal * (RAY.dir . CURRENT_OBJECT->normal);
	NEW_RAY.ori = CURRENT_OBJECT->hit_point;
	
	...

	if ((NEW_RAY.dir . CURRENT_OBJECT->normal) > 0.0)
		NEXT_OBJECT_COLOR = compute_objects(..., NEW_RAY, ...);
	else
		NEXT_OBJECT_COLOR = CURRENT_OBJECT->color;

	...
	
	return (NEXT_OBJECT_COLOR * CURRENT_OBJECT->color);
}
  • Refraction
double		schlick(double cosine, double index)
{
	double r;

	r = (1.0 - index) / (double)(1 + index);
	r = r * r;
	r = (r + ((1 - r) * pow((1 - cosine), 5.0)));
	return (r);
}

t_ray		launch_ray(..., t_obj *CURRENT_OBJECT, t_vector dir, double cosine)
{
	...

	dt = vector_normalize(dir) . CURRENT_OBJECT->normal;
	discriminant = 1.0 - (ni_nt * ni_nt * (1 - (dt * dt)));
	
	if (discriminant > (double)0.0)
		reflect_probe = schlick(cosine, CURRENT_OBJECT->refraction);
	else
		reflect_probe = 1.0;
	
	if (ran_double() > reflect_probe)
	{
		NEW_RAY.dir = (dir - CURRENT_OBJECT->normal * dt * e->ni_nt) - (CURRENT_OBJECT->normal * sqrt(discriminant));
		NEW_RAY.ori = CURRENT_OBJECT->hit_point;
	}
	else
	{
		NEW_RAY.dir = dir - (CURRENT_OBJECT->normal * 2.0 * (dir . CURRENT_OBJECT->normal));
		NEW_RAY.ori = CURRENT_OBJECT->hit_point;
	}
	
	return (NEW_RAY);
}

t_vector	refraction(..., t_ray RAY, t_obj *CURRENT_OBJECT)
{
	...
	
	if (RAY.dir . CURRENT_OBJECT->normal) > 0.0)
	{
		ni_nt = CURRENT_OBJECT->refraction;
		cosine = CURRENT_OBJECT->refraction * (RAY.dir . CURRENT_OBJECT->normal) / len(RAY.dir);
	}
	else
	{
		ni_nt = 1.0 / CURRENT_OBJECT->refraction;
		cosine = -(RAY.dir . CURRENT_OBJECT->normal) / len(RAY.dir);
	}

	NEW_RAY = launch_ray(..., CURRENT_OBJECT, RAY.dir, cosine);
	
	...
	
	return (compute_objects(..., NEW_RAY, ...) * CURRENT_OBJECT->color));
}

Those 4 functions are the core of the project. On top of it, you can add

  • the object detection (ray-object intersection with help of quadratic)
  • color mapping, transparency mapping, skybox
  • perlin noise
  • ...
  • and so on

Sources

Peter Shirley's guide (do not hesitate to buy to support, it is worth it) :

Some Ray Tracing concept :

Ray-Object intersection :

Torus :

UV mapping :

A bunch of vector operation :

Addendum, for an update :