src/raytracer.cc
branchpyrit
changeset 0 3547b885df7e
child 4 c73bc405ee7a
equal deleted inserted replaced
-1:000000000000 0:3547b885df7e
       
     1 /*
       
     2  * C++ RayTracer
       
     3  * file: raytracer.cc
       
     4  *
       
     5  * Radek Brich, 2006
       
     6  */
       
     7 
       
     8 #include <stdio.h>
       
     9 #include <malloc.h>
       
    10 #include <float.h>
       
    11 #include "raytracer.h"
       
    12 
       
    13 // Hammersley spherical point distribution
       
    14 // http://www.cse.cuhk.edu.hk/~ttwong/papers/udpoint/udpoints.html
       
    15 Vector3 Raytracer::SphereDistribute(int i, int n, float extent, Vector3 &normal)
       
    16 {
       
    17 	float p, t, st, phi, phirad;
       
    18 	int kk;
       
    19 
       
    20 	t = 0;
       
    21 	for (p=0.5, kk=i; kk; p*=0.5, kk>>=1)
       
    22 	if (kk & 1)
       
    23 		t += p;
       
    24 	t = 1.0 + (t - 1.0)*extent;
       
    25 
       
    26 	phi = (i + 0.5) / n;
       
    27 	phirad =  phi * 2.0 * M_PI;
       
    28 
       
    29 	st = sqrt(1.0 - t*t);
       
    30 
       
    31 	float x, y, z, xx, yy, zz, q;
       
    32 	x = st * cos(phirad);
       
    33 	y = st * sin(phirad);
       
    34 	z = t;
       
    35 
       
    36 	// rotate against Y axis
       
    37 	q = acos(normal.z);
       
    38 	zz = z*cos(q) - x*sin(q);
       
    39 	xx = z*sin(q) + x*cos(q);
       
    40 	yy = y;
       
    41 
       
    42 	// rotate against Z axis
       
    43 	q = atan2f(normal.y, normal.x);
       
    44 	x = xx*cos(q) - yy*sin(q);
       
    45 	y = xx*sin(q) + yy*cos(q);
       
    46 	z = zz;
       
    47 
       
    48 	return Vector3(x, y, z);
       
    49 }
       
    50 
       
    51 inline Shape *Raytracer::nearest_intersection(const Shape *origin_shape, const Ray &ray,
       
    52 	float &nearest_distance)
       
    53 {
       
    54 	Shape *nearest_shape = NULL;
       
    55 	ShapeList::iterator shape;
       
    56 	for (shape = shapes.begin(); shape != shapes.end(); shape++)
       
    57 		if (*shape != origin_shape && (*shape)->intersect(ray, nearest_distance))
       
    58 			nearest_shape = *shape;
       
    59 	return nearest_shape;
       
    60 }
       
    61 
       
    62 // ---- tyto dve funkce budou v budouci verzi metody objektu PhongShader
       
    63 
       
    64 // calculate shader function
       
    65 // P is point of intersection, N normal in this point
       
    66 Colour PhongShader_ambient(Material &mat, Vector3 &P)
       
    67 {
       
    68 	Colour col = mat.texture.colour; //mat.texture.evaluate(P);
       
    69 
       
    70 	// ambient
       
    71 	return mat.ambient * col;
       
    72 }
       
    73 
       
    74 /*
       
    75  P is point of intersection,
       
    76  N normal in this point,
       
    77  R direction of reflected ray,
       
    78  V direction to the viewer
       
    79 */
       
    80 Colour PhongShader_calculate(Material &mat, Vector3 &P, Vector3 &N, Vector3 &R, Vector3 &V,
       
    81 	Light &light)
       
    82 {
       
    83 	Colour I = Colour();
       
    84 	Vector3 L = light.pos - P;
       
    85 	L.normalize();
       
    86 	float L_dot_N = dot(L, N);
       
    87 	float R_dot_V = dot(R, V);
       
    88 
       
    89 	Colour col = mat.texture.colour; //mat.texture.evaluate(P);
       
    90 
       
    91 	// diffuse
       
    92 	I = mat.diffuse * col * light.colour * L_dot_N;
       
    93 
       
    94 	// specular
       
    95 	if (R_dot_V > 0)
       
    96 		I += mat.specular * light.colour * powf(R_dot_V, mat.shininess);
       
    97 	return I;
       
    98 }
       
    99 
       
   100 Colour Raytracer::raytrace(Ray &ray, int depth, Shape *origin_shape)
       
   101 {
       
   102 	float nearest_distance = FLT_MAX; //Infinity
       
   103 	Shape *nearest_shape = nearest_intersection(origin_shape, ray, nearest_distance);
       
   104 
       
   105 	if (nearest_shape == NULL) {
       
   106 		return bg_colour;
       
   107 	} else {
       
   108 		Colour acc = Colour();
       
   109 		Vector3 P = ray.a + ray.dir * nearest_distance; // point of intersection
       
   110 		Vector3 normal = nearest_shape->normal(P);
       
   111 		acc = PhongShader_ambient(*nearest_shape->material, P);
       
   112 
       
   113 		vector<Light*>::iterator light;
       
   114 		for (light = lights.begin(); light != lights.end(); light++) {
       
   115 			Vector3 jo, L = (*light)->pos - P; // direction vector to light
       
   116 			L.normalize();
       
   117 			float L_dot_N = dot(L, normal);
       
   118 			if (L_dot_N > 0) {
       
   119 				// test if this light is occluded (sharp shadows)
       
   120 				if ((*light)->shadows) {
       
   121 					Ray shadow_ray = Ray(P, L);
       
   122 					float dist = FLT_MAX;
       
   123 					if (nearest_intersection(nearest_shape, shadow_ray, dist))
       
   124 						continue;
       
   125 				}
       
   126 
       
   127 				// shading function
       
   128 				Vector3 R = L - 2.0 * L_dot_N * normal;
       
   129 				acc += PhongShader_calculate(*nearest_shape->material,
       
   130 					P, normal, R, ray.dir, **light);
       
   131 			}
       
   132 		}
       
   133 
       
   134 		// reflection
       
   135 		int trace_max_depth = 4;
       
   136 		Vector3 newdir = ray.dir - 2.0 * dot(ray.dir, normal) * normal;
       
   137 		if (depth < trace_max_depth && nearest_shape->material->reflection > 0.01) {
       
   138 			Ray newray = Ray(P, newdir);
       
   139 			Colour refl_col = raytrace(newray, depth + 1, nearest_shape);
       
   140 			acc += nearest_shape->material->reflection * refl_col;
       
   141 		}
       
   142 
       
   143 		// refraction
       
   144 		/* ... */
       
   145 
       
   146 		// ambient occlusion
       
   147 		if (ao_samples)
       
   148 		{
       
   149 			float miss = 0;
       
   150 			for (int i = 0; i < ao_samples; i++) {
       
   151 				Vector3 dir = SphereDistribute(i, ao_samples, ao_angle, normal);
       
   152 				Ray ao_ray = Ray(P, dir);
       
   153 				float dist = ao_distance;
       
   154 				Shape *shape_in_way = nearest_intersection(nearest_shape, ao_ray, dist);
       
   155 				if (shape_in_way == NULL)
       
   156 					miss += 1.0;
       
   157 				else
       
   158 					miss += dist / ao_distance;
       
   159 			}
       
   160 			float ao_intensity = miss / ao_samples;
       
   161 			acc = acc * ao_intensity;
       
   162 		}
       
   163 
       
   164 		return acc;
       
   165 	}
       
   166 }
       
   167 
       
   168 float *Raytracer::render(int w, int h)
       
   169 {
       
   170 	int x, y;
       
   171 	float *data, *iter;
       
   172 
       
   173 	data = (float *) malloc(w*h*3*sizeof(float));
       
   174 	if (!data)
       
   175 		return NULL;
       
   176 
       
   177 	float startx = -1.0 * 4, starty = (float)h/w * 4;
       
   178 	float dx = -2*startx/w, dy = -2*starty/h;
       
   179 	float vx, vy;
       
   180 
       
   181 	//srand(time(NULL));
       
   182 
       
   183 	// eye - viewing point
       
   184 	Vector3 eye(0, 0, -5);
       
   185 
       
   186 	// for each pixel...
       
   187 	iter = data;
       
   188 	for (vy = starty, y = 0; y < h; y++) {
       
   189 		vx = startx;
       
   190 		for (x = 0; x < w; x++) {
       
   191 			// generate a ray from eye passing through this pixel
       
   192 #if 1
       
   193 			// no oversampling
       
   194 			Vector3 dir = Vector3(vx, vy, 0) - eye;
       
   195 			dir.normalize();
       
   196 			Ray ray(eye, dir);
       
   197 			Colour c = raytrace(ray, 0, NULL);
       
   198 #else
       
   199 			// 5x oversampling
       
   200 			Vector3 dir = Vector3();
       
   201 			Colour c = Colour();
       
   202 
       
   203 			for (int i = 0; i < 5; i++)
       
   204 			{
       
   205 				float osax[] = {0.0, -0.4, +0.4, +0.4, -0.4};
       
   206 				float osay[] = {0.0, -0.4, -0.4, +0.4, +0.4};
       
   207 				dir = Vector3(vx + osax[i]*dx,
       
   208 					vy + osay[i]*dy , 0) - eye;
       
   209 				dir.normalize();
       
   210 				Ray ray(eye, dir);
       
   211 				c += raytrace(ray, 0, NULL);
       
   212 			}
       
   213 			c = c * (1./5);
       
   214 #endif
       
   215 			*iter++ = c.r;
       
   216 			*iter++ = c.g;
       
   217 			*iter++ = c.b;
       
   218 			vx += dx;
       
   219 		}
       
   220 		vy += dy;
       
   221 		printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b%2d%% done (row %4d)", y*100/(h-1), y);
       
   222 	}
       
   223 	printf("\n");
       
   224 	return data;
       
   225 }
       
   226 
       
   227 void Raytracer::addshape(Shape *shape)
       
   228 {
       
   229 	shapes.push_back(shape);
       
   230 }
       
   231 
       
   232 void Raytracer::addlight(Light *light)
       
   233 {
       
   234 	lights.push_back(light);
       
   235 }
       
   236 
       
   237 void Raytracer::ambientocclusion(int samples, float distance, float angle)
       
   238 {
       
   239 	ao_samples = samples;
       
   240 	ao_distance = distance;
       
   241 	ao_angle = angle;
       
   242 	if (ao_distance == 0)
       
   243 		/* 0 ==> Inf */
       
   244 		ao_distance = FLT_MAX;
       
   245 }