diff -r 000000000000 -r 3547b885df7e src/raytracer.cc --- /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 +#include +#include +#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::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; +}