use python-config, strip python version from demos, change [PyRit] to [pyrit] -- should use unix name everywhere
/*
* 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;
}