src/raytracer.cc
branchpyrit
changeset 82 930a2d3ecaed
parent 77 dbe8438d5dca
child 83 e3a2a5b26abb
equal deleted inserted replaced
81:9dbb9c8c115b 82:930a2d3ecaed
    25  */
    25  */
    26 
    26 
    27 #include <pthread.h>
    27 #include <pthread.h>
    28 #include <stdio.h>
    28 #include <stdio.h>
    29 #include <stdlib.h>
    29 #include <stdlib.h>
       
    30 #include <assert.h>
    30 #include "raytracer.h"
    31 #include "raytracer.h"
    31 
    32 
    32 int pyrit_verbosity = 2;
    33 int pyrit_verbosity = 2;
    33 
    34 
    34 // Hammersley spherical point distribution
    35 // Hammersley spherical point distribution
   113 	if (R_dot_V > 0)
   114 	if (R_dot_V > 0)
   114 		I += mat.specular * light.colour * powf(R_dot_V, mat.shininess);
   115 		I += mat.specular * light.colour * powf(R_dot_V, mat.shininess);
   115 	return I;
   116 	return I;
   116 }
   117 }
   117 
   118 
       
   119 Colour Raytracer::shader_evalulate(Ray &ray, int depth, Shape *origin_shape,
       
   120 	Float nearest_distance, Shape *nearest_shape)
       
   121 {
       
   122 	Colour col = Colour();
       
   123 	Vector3 P = ray.o + ray.dir * nearest_distance; // point of intersection
       
   124 	Vector3 normal = nearest_shape->normal(P);
       
   125 	bool from_inside = false;
       
   126 
       
   127 	// make shapes double sided
       
   128 	if (dot(normal, ray.dir) > 0.0)
       
   129 	{
       
   130 		normal = - normal;
       
   131 		from_inside = true;
       
   132 	}
       
   133 
       
   134 	col = PhongShader_ambient(*nearest_shape->material, P);
       
   135 
       
   136 	vector<Light*>::iterator light;
       
   137 	for (light = lights.begin(); light != lights.end(); light++) {
       
   138 		Vector3 jo, L = (*light)->pos - P; // direction vector to light
       
   139 		L.normalize();
       
   140 		Float L_dot_N = dot(L, normal);
       
   141 		if (L_dot_N > 0) {
       
   142 			// test if this light is occluded (sharp shadows)
       
   143 			if ((*light)->cast_shadows) {
       
   144 				Ray shadow_ray = Ray(P, L);
       
   145 				Float dist = FLT_MAX;
       
   146 				if (top->nearest_intersection(nearest_shape, shadow_ray, dist))
       
   147 					continue;
       
   148 			}
       
   149 
       
   150 			// shading function
       
   151 			Vector3 R = L - 2.0 * L_dot_N * normal;
       
   152 			col += PhongShader_calculate(*nearest_shape->material,
       
   153 				P, normal, R, ray.dir, **light);
       
   154 		}
       
   155 	}
       
   156 
       
   157 	if (depth < max_depth)
       
   158 	{
       
   159 		Colour trans_col, refl_col;
       
   160 		Float trans = nearest_shape->material->transmissivity;
       
   161 		Float refl = nearest_shape->material->reflectivity;
       
   162 		const Float cos_i = - dot(normal, ray.dir);
       
   163 
       
   164 		// reflection
       
   165 		if (refl > 0.01)
       
   166 		{
       
   167 			Vector3 newdir = ray.dir + 2.0 * cos_i * normal;
       
   168 			Ray newray = Ray(P, newdir);
       
   169 			refl_col = raytrace(newray, depth + 1, nearest_shape);
       
   170 		}
       
   171 
       
   172 		// refraction
       
   173 		if (trans > 0.01)
       
   174 		{
       
   175 			Float n, n1, n2;
       
   176 			if (from_inside)
       
   177 			{
       
   178 				n1 = nearest_shape->material->refract_index;
       
   179 				n2 = 1.0;
       
   180 				n = n1;
       
   181 			}
       
   182 			else
       
   183 			{
       
   184 				n1 = 1.0;
       
   185 				n2 = nearest_shape->material->refract_index;
       
   186 				n = 1.0 / n2;
       
   187 			}
       
   188 			const Float sin2_t = n*n * (1 - cos_i*cos_i);
       
   189 			if (sin2_t >= 1.0)
       
   190 			{
       
   191 					// totally reflected
       
   192 					refl += trans;
       
   193 					trans = 0;
       
   194 			}
       
   195 			else
       
   196 			{
       
   197 				const Float cos_t = sqrtf(1 - sin2_t);
       
   198 				const Float Rdiv = 1.0/(n1*cos_i + n2*cos_t);
       
   199 				const Float Rper = (n1*cos_i - n2*cos_t)*Rdiv;
       
   200 				const Float Rpar = (n2*cos_i - n1*cos_t)*Rdiv;
       
   201 				const Float R = (Rper*Rper + Rpar*Rpar)/2;
       
   202 				refl += R*trans;
       
   203 				trans = (1-R)*trans;
       
   204 				Vector3 newdir = n * ray.dir + (n*cos_i - cos_t) * normal;
       
   205 				Ray newray = Ray(P + 0.001*newdir, newdir);
       
   206 				trans_col = raytrace(newray, depth + 1, NULL);
       
   207 			}
       
   208 		}
       
   209 		col = (1-refl-trans)*col + refl*refl_col + trans*trans_col;
       
   210 	}
       
   211 
       
   212 	// ambient occlusion
       
   213 	if (!from_inside && ao_samples)
       
   214 	{
       
   215 		Float miss = 0;
       
   216 		for (int i = 0; i < ao_samples; i++) {
       
   217 			Vector3 dir = SphereDistribute(i, ao_samples, ao_angle, normal);
       
   218 			Ray ao_ray = Ray(P, dir);
       
   219 			Float dist = ao_distance;
       
   220 			Shape *shape_in_way = top->nearest_intersection(nearest_shape, ao_ray, dist);
       
   221 			if (shape_in_way == NULL)
       
   222 				miss += 1.0;
       
   223 			else
       
   224 				miss += dist / ao_distance;
       
   225 		}
       
   226 		Float ao_intensity = miss / ao_samples;
       
   227 		col = col * ao_intensity;
       
   228 	}
       
   229 
       
   230 	return col;
       
   231 }
       
   232 
   118 Colour Raytracer::raytrace(Ray &ray, int depth, Shape *origin_shape)
   233 Colour Raytracer::raytrace(Ray &ray, int depth, Shape *origin_shape)
   119 {
   234 {
   120 	Float nearest_distance = Inf;
   235 	Float nearest_distance = Inf;
   121 	Shape *nearest_shape = top->nearest_intersection(origin_shape, ray, nearest_distance);
   236 	Shape *nearest_shape = top->nearest_intersection(origin_shape, ray, nearest_distance);
   122 
   237 
   123 	if (nearest_shape == NULL) {
   238 	if (nearest_shape == NULL)
       
   239 	{
   124 		return bg_colour;
   240 		return bg_colour;
   125 	} else {
   241 	}
   126 		Colour col = Colour();
   242 	else
   127 		Vector3 P = ray.o + ray.dir * nearest_distance; // point of intersection
   243 	{
   128 		Vector3 normal = nearest_shape->normal(P);
   244 		return shader_evalulate(ray, depth, origin_shape, nearest_distance, nearest_shape);
   129 		bool from_inside = false;
   245 	}
   130 
   246 }
   131 		// make shapes double sided
   247 
   132 		if (dot(normal, ray.dir) > 0.0)
   248 void Raytracer::raytracePacket(Ray *rays, Colour *results)
   133 		{
   249 {
   134 			normal = - normal;
   250 	Float nearest_distances[4] = {Inf,Inf,Inf,Inf};
   135 			from_inside = true;
   251 	Shape *nearest_shapes[4];
   136 		}
   252 	static const Shape *origin_shapes[4] = {NULL, NULL, NULL, NULL};
   137 
   253 	top->packet_intersection(origin_shapes, rays, nearest_distances, nearest_shapes);
   138 		col = PhongShader_ambient(*nearest_shape->material, P);
   254 
   139 
   255 	for (int i = 0; i < 4; i++)
   140 		vector<Light*>::iterator light;
   256 		if (nearest_shapes[i] == NULL)
   141 		for (light = lights.begin(); light != lights.end(); light++) {
   257 			results[i] = bg_colour;
   142 			Vector3 jo, L = (*light)->pos - P; // direction vector to light
   258 		else
   143 			L.normalize();
   259 			results[i] = shader_evalulate(rays[i], 0, NULL,
   144 			Float L_dot_N = dot(L, normal);
   260 				nearest_distances[i], nearest_shapes[i]);
   145 			if (L_dot_N > 0) {
       
   146 				// test if this light is occluded (sharp shadows)
       
   147 				if ((*light)->cast_shadows) {
       
   148 					Ray shadow_ray = Ray(P, L);
       
   149 					Float dist = FLT_MAX;
       
   150 					if (top->nearest_intersection(nearest_shape, shadow_ray, dist))
       
   151 						continue;
       
   152 				}
       
   153 
       
   154 				// shading function
       
   155 				Vector3 R = L - 2.0 * L_dot_N * normal;
       
   156 				col += PhongShader_calculate(*nearest_shape->material,
       
   157 					P, normal, R, ray.dir, **light);
       
   158 			}
       
   159 		}
       
   160 
       
   161 		if (depth < max_depth)
       
   162 		{
       
   163 			Colour trans_col, refl_col;
       
   164 			Float trans = nearest_shape->material->transmissivity;
       
   165 			Float refl = nearest_shape->material->reflectivity;
       
   166 			const Float cos_i = - dot(normal, ray.dir);
       
   167 
       
   168 			// reflection
       
   169 			if (refl > 0.01)
       
   170 			{
       
   171 				Vector3 newdir = ray.dir + 2.0 * cos_i * normal;
       
   172 				Ray newray = Ray(P, newdir);
       
   173 				refl_col = raytrace(newray, depth + 1, nearest_shape);
       
   174 			}
       
   175 
       
   176 			// refraction
       
   177 		 	if (trans > 0.01)
       
   178 			{
       
   179 				Float n, n1, n2;
       
   180 				if (from_inside)
       
   181 				{
       
   182 					n1 = nearest_shape->material->refract_index;
       
   183 					n2 = 1.0;
       
   184 					n = n1;
       
   185 				}
       
   186 				else
       
   187 				{
       
   188 					n1 = 1.0;
       
   189 					n2 = nearest_shape->material->refract_index;
       
   190 					n = 1.0 / n2;
       
   191 				}
       
   192 				const Float sin2_t = n*n * (1 - cos_i*cos_i);
       
   193 				if (sin2_t >= 1.0)
       
   194 				{
       
   195 					 // totally reflected
       
   196 					 refl += trans;
       
   197 					 trans = 0;
       
   198 				}
       
   199 				else
       
   200 				{
       
   201 					const Float cos_t = sqrtf(1 - sin2_t);
       
   202 					const Float Rdiv = 1.0/(n1*cos_i + n2*cos_t);
       
   203 					const Float Rper = (n1*cos_i - n2*cos_t)*Rdiv;
       
   204 					const Float Rpar = (n2*cos_i - n1*cos_t)*Rdiv;
       
   205 					const Float R = (Rper*Rper + Rpar*Rpar)/2;
       
   206 					refl += R*trans;
       
   207 					trans = (1-R)*trans;
       
   208 					Vector3 newdir = n * ray.dir + (n*cos_i - cos_t) * normal;
       
   209 					Ray newray = Ray(P + 0.001*newdir, newdir);
       
   210 					trans_col = raytrace(newray, depth + 1, NULL);
       
   211 				}
       
   212 			}
       
   213 			col = (1-refl-trans)*col + refl*refl_col + trans*trans_col;
       
   214 		}
       
   215 
       
   216 		// ambient occlusion
       
   217 		if (!from_inside && ao_samples)
       
   218 		{
       
   219 			Float miss = 0;
       
   220 			for (int i = 0; i < ao_samples; i++) {
       
   221 				Vector3 dir = SphereDistribute(i, ao_samples, ao_angle, normal);
       
   222 				Ray ao_ray = Ray(P, dir);
       
   223 				Float dist = ao_distance;
       
   224 				Shape *shape_in_way = top->nearest_intersection(nearest_shape, ao_ray, dist);
       
   225 				if (shape_in_way == NULL)
       
   226 					miss += 1.0;
       
   227 				else
       
   228 					miss += dist / ao_distance;
       
   229 			}
       
   230 			Float ao_intensity = miss / ao_samples;
       
   231 			col = col * ao_intensity;
       
   232 		}
       
   233 
       
   234 		return col;
       
   235 	}
       
   236 }
   261 }
   237 
   262 
   238 void *Raytracer::raytrace_worker(void *d)
   263 void *Raytracer::raytrace_worker(void *d)
   239 {
   264 {
   240 	static const int my_queue_size = 256;
   265 	static const int my_queue_size = 256;
   241 	Raytracer *rt = (Raytracer*)d;
   266 	Raytracer *rt = (Raytracer*)d;
   242 	Sample my_queue[my_queue_size];
   267 	Sample my_queue[my_queue_size];
   243 	Colour my_colours[my_queue_size];
   268 	Colour my_colours[my_queue_size];
   244 	int my_count;
   269 	int my_count;
   245 	Ray ray;
   270 	Ray ray, rays[4];
       
   271 	const bool can_use_packets = (rt->use_packets && rt->sampler->packetableSamples());
   246 	for (;;)
   272 	for (;;)
   247 	{
   273 	{
   248 		pthread_mutex_lock(&rt->sample_queue_mutex);
   274 		pthread_mutex_lock(&rt->sample_queue_mutex);
   249 		while (rt->sample_queue_count == 0)
   275 		while (rt->sample_queue_count == 0)
   250 		{
   276 		{
   280 		if (rt->sample_queue_count <= my_queue_size*2)
   306 		if (rt->sample_queue_count <= my_queue_size*2)
   281 			pthread_cond_signal(&rt->worker_ready_cond);
   307 			pthread_cond_signal(&rt->worker_ready_cond);
   282 		pthread_mutex_unlock(&rt->sample_queue_mutex);
   308 		pthread_mutex_unlock(&rt->sample_queue_mutex);
   283 
   309 
   284 		// do the work
   310 		// do the work
   285 		for (int i = 0; i < my_count; i++)
   311 		if (can_use_packets)
   286 		{
   312 		{
   287 			ray = rt->camera->makeRay(my_queue[i]);
   313 			// packet ray tracing
   288 			my_colours[i] = rt->raytrace(ray, 0, NULL);
   314 			assert((my_count % 4) == 0);
       
   315 			for (int i = 0; i < my_count; i+=4)
       
   316 			{
       
   317 				rt->camera->makeRayPacket(my_queue + i, rays);
       
   318 				rt->raytracePacket(rays, my_colours + i);
       
   319 			}
       
   320 		}
       
   321 		else
       
   322 		{
       
   323 			// single ray tracing
       
   324 			for (int i = 0; i < my_count; i++)
       
   325 			{
       
   326 				ray = rt->camera->makeRay(my_queue[i]);
       
   327 				my_colours[i] = rt->raytrace(ray, 0, NULL);
       
   328 			}
   289 		}
   329 		}
   290 
   330 
   291 		// save the results
   331 		// save the results
   292 		pthread_mutex_lock(&rt->sampler_mutex);
   332 		pthread_mutex_lock(&rt->sampler_mutex);
   293 		for (int i = 0; i < my_count; i++)
   333 		for (int i = 0; i < my_count; i++)