--- /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;
+}