src/raytracer.cc
branchpyrit
changeset 0 3547b885df7e
child 4 c73bc405ee7a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/raytracer.cc	Thu Oct 25 16:40:22 2007 +0200
@@ -0,0 +1,245 @@
+/*
+ * C++ RayTracer
+ * file: raytracer.cc
+ *
+ * Radek Brich, 2006
+ */
+
+#include <stdio.h>
+#include <malloc.h>
+#include <float.h>
+#include "raytracer.h"
+
+// Hammersley spherical point distribution
+// http://www.cse.cuhk.edu.hk/~ttwong/papers/udpoint/udpoints.html
+Vector3 Raytracer::SphereDistribute(int i, int n, float extent, Vector3 &normal)
+{
+	float p, t, st, phi, phirad;
+	int kk;
+
+	t = 0;
+	for (p=0.5, kk=i; kk; p*=0.5, kk>>=1)
+	if (kk & 1)
+		t += p;
+	t = 1.0 + (t - 1.0)*extent;
+
+	phi = (i + 0.5) / n;
+	phirad =  phi * 2.0 * M_PI;
+
+	st = sqrt(1.0 - t*t);
+
+	float x, y, z, xx, yy, zz, q;
+	x = st * cos(phirad);
+	y = st * sin(phirad);
+	z = t;
+
+	// rotate against Y axis
+	q = acos(normal.z);
+	zz = z*cos(q) - x*sin(q);
+	xx = z*sin(q) + x*cos(q);
+	yy = y;
+
+	// rotate against Z axis
+	q = atan2f(normal.y, normal.x);
+	x = xx*cos(q) - yy*sin(q);
+	y = xx*sin(q) + yy*cos(q);
+	z = zz;
+
+	return Vector3(x, y, z);
+}
+
+inline Shape *Raytracer::nearest_intersection(const Shape *origin_shape, const Ray &ray,
+	float &nearest_distance)
+{
+	Shape *nearest_shape = NULL;
+	ShapeList::iterator shape;
+	for (shape = shapes.begin(); shape != shapes.end(); shape++)
+		if (*shape != origin_shape && (*shape)->intersect(ray, nearest_distance))
+			nearest_shape = *shape;
+	return nearest_shape;
+}
+
+// ---- tyto dve funkce budou v budouci verzi metody objektu PhongShader
+
+// calculate shader function
+// P is point of intersection, N normal in this point
+Colour PhongShader_ambient(Material &mat, Vector3 &P)
+{
+	Colour col = mat.texture.colour; //mat.texture.evaluate(P);
+
+	// ambient
+	return mat.ambient * col;
+}
+
+/*
+ P is point of intersection,
+ N normal in this point,
+ R direction of reflected ray,
+ V direction to the viewer
+*/
+Colour PhongShader_calculate(Material &mat, Vector3 &P, Vector3 &N, Vector3 &R, Vector3 &V,
+	Light &light)
+{
+	Colour I = Colour();
+	Vector3 L = light.pos - P;
+	L.normalize();
+	float L_dot_N = dot(L, N);
+	float R_dot_V = dot(R, V);
+
+	Colour col = mat.texture.colour; //mat.texture.evaluate(P);
+
+	// diffuse
+	I = mat.diffuse * col * light.colour * L_dot_N;
+
+	// specular
+	if (R_dot_V > 0)
+		I += mat.specular * light.colour * powf(R_dot_V, mat.shininess);
+	return I;
+}
+
+Colour Raytracer::raytrace(Ray &ray, int depth, Shape *origin_shape)
+{
+	float nearest_distance = FLT_MAX; //Infinity
+	Shape *nearest_shape = nearest_intersection(origin_shape, ray, nearest_distance);
+
+	if (nearest_shape == NULL) {
+		return bg_colour;
+	} else {
+		Colour acc = Colour();
+		Vector3 P = ray.a + ray.dir * nearest_distance; // point of intersection
+		Vector3 normal = nearest_shape->normal(P);
+		acc = PhongShader_ambient(*nearest_shape->material, P);
+
+		vector<Light*>::iterator light;
+		for (light = lights.begin(); light != lights.end(); light++) {
+			Vector3 jo, L = (*light)->pos - P; // direction vector to light
+			L.normalize();
+			float L_dot_N = dot(L, normal);
+			if (L_dot_N > 0) {
+				// test if this light is occluded (sharp shadows)
+				if ((*light)->shadows) {
+					Ray shadow_ray = Ray(P, L);
+					float dist = FLT_MAX;
+					if (nearest_intersection(nearest_shape, shadow_ray, dist))
+						continue;
+				}
+
+				// shading function
+				Vector3 R = L - 2.0 * L_dot_N * normal;
+				acc += PhongShader_calculate(*nearest_shape->material,
+					P, normal, R, ray.dir, **light);
+			}
+		}
+
+		// reflection
+		int trace_max_depth = 4;
+		Vector3 newdir = ray.dir - 2.0 * dot(ray.dir, normal) * normal;
+		if (depth < trace_max_depth && nearest_shape->material->reflection > 0.01) {
+			Ray newray = Ray(P, newdir);
+			Colour refl_col = raytrace(newray, depth + 1, nearest_shape);
+			acc += nearest_shape->material->reflection * refl_col;
+		}
+
+		// refraction
+		/* ... */
+
+		// ambient occlusion
+		if (ao_samples)
+		{
+			float miss = 0;
+			for (int i = 0; i < ao_samples; i++) {
+				Vector3 dir = SphereDistribute(i, ao_samples, ao_angle, normal);
+				Ray ao_ray = Ray(P, dir);
+				float dist = ao_distance;
+				Shape *shape_in_way = nearest_intersection(nearest_shape, ao_ray, dist);
+				if (shape_in_way == NULL)
+					miss += 1.0;
+				else
+					miss += dist / ao_distance;
+			}
+			float ao_intensity = miss / ao_samples;
+			acc = acc * ao_intensity;
+		}
+
+		return acc;
+	}
+}
+
+float *Raytracer::render(int w, int h)
+{
+	int x, y;
+	float *data, *iter;
+
+	data = (float *) malloc(w*h*3*sizeof(float));
+	if (!data)
+		return NULL;
+
+	float startx = -1.0 * 4, starty = (float)h/w * 4;
+	float dx = -2*startx/w, dy = -2*starty/h;
+	float vx, vy;
+
+	//srand(time(NULL));
+
+	// eye - viewing point
+	Vector3 eye(0, 0, -5);
+
+	// for each pixel...
+	iter = data;
+	for (vy = starty, y = 0; y < h; y++) {
+		vx = startx;
+		for (x = 0; x < w; x++) {
+			// generate a ray from eye passing through this pixel
+#if 1
+			// no oversampling
+			Vector3 dir = Vector3(vx, vy, 0) - eye;
+			dir.normalize();
+			Ray ray(eye, dir);
+			Colour c = raytrace(ray, 0, NULL);
+#else
+			// 5x oversampling
+			Vector3 dir = Vector3();
+			Colour c = Colour();
+
+			for (int i = 0; i < 5; i++)
+			{
+				float osax[] = {0.0, -0.4, +0.4, +0.4, -0.4};
+				float osay[] = {0.0, -0.4, -0.4, +0.4, +0.4};
+				dir = Vector3(vx + osax[i]*dx,
+					vy + osay[i]*dy , 0) - eye;
+				dir.normalize();
+				Ray ray(eye, dir);
+				c += raytrace(ray, 0, NULL);
+			}
+			c = c * (1./5);
+#endif
+			*iter++ = c.r;
+			*iter++ = c.g;
+			*iter++ = c.b;
+			vx += dx;
+		}
+		vy += dy;
+		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);
+	}
+	printf("\n");
+	return data;
+}
+
+void Raytracer::addshape(Shape *shape)
+{
+	shapes.push_back(shape);
+}
+
+void Raytracer::addlight(Light *light)
+{
+	lights.push_back(light);
+}
+
+void Raytracer::ambientocclusion(int samples, float distance, float angle)
+{
+	ao_samples = samples;
+	ao_distance = distance;
+	ao_angle = angle;
+	if (ao_distance == 0)
+		/* 0 ==> Inf */
+		ao_distance = FLT_MAX;
+}