naive color driven sub-sampling
slightly optimized KdTree::nearest_intersection
fixed bug in Box::intersect
fixed stripes on spheres in spheres_ao.py (was caused by AO distance)
new KdTree property: max_depth
minor changes in realtime.py
--- a/TODO Thu Nov 29 18:30:16 2007 +0100
+++ b/TODO Fri Nov 30 00:44:51 2007 +0100
@@ -5,7 +5,7 @@
* update Python binding, Camera, new classes
* more complex demos
* check/update triangle routines
-
+ * namespace
New Classes?
============
--- a/ccdemos/realtime.cc Thu Nov 29 18:30:16 2007 +0100
+++ b/ccdemos/realtime.cc Fri Nov 30 00:44:51 2007 +0100
@@ -2,8 +2,8 @@
#include "raytracer.h"
-int w = 512;
-int h = 200;
+int w = 480;
+int h = 288;
float *render_buffer;
Raytracer rt;
@@ -13,11 +13,9 @@
{
rt.render(w, h, render_buffer);
- if ( SDL_MUSTLOCK(screen) ) {
- if ( SDL_LockSurface(screen) < 0 ) {
+ if (SDL_MUSTLOCK(screen))
+ if (SDL_LockSurface(screen) < 0)
return;
- }
- }
Uint32 *bufp = (Uint32 *)screen->pixels;
unsigned char c[3];
@@ -34,12 +32,13 @@
bufp++;
}
- if ( SDL_MUSTLOCK(screen) ) {
+ if (SDL_MUSTLOCK(screen))
SDL_UnlockSurface(screen);
- }
- SDL_UpdateRect(screen, 0, 0, w, h);
- SDL_Flip(screen);
+ if (screen->flags & SDL_DOUBLEBUF)
+ SDL_Flip(screen);
+ else
+ SDL_UpdateRect(screen, 0, 0, w, h);
}
int main()
@@ -64,25 +63,29 @@
render_buffer = (float *) malloc(w*h*3*sizeof(float));
rt.setThreads(2);
+ rt.setMaxDepth(1);
KdTree top;
rt.setTop(&top);
Light light1(Vector3(2.0, -5.0, -5.0), Colour(0.7, 0.3, 0.6));
+ light1.castShadows(false);
rt.addlight(&light1);
Light light2(Vector3(-2.0, 10.0, 2.0), Colour(0.4, 0.6, 0.3));
+ light2.castShadows(false);
rt.addlight(&light2);
Material mat_sph(Colour(1.0, 1.0, 1.0));
- for (int y=0; y<20; y++)
- for (int x=0; x<20; x++)
- rt.addshape(new Sphere(Vector3(x-10, (float)random()/RAND_MAX*5.0, y-10), 0.45, &mat_sph));
+ for (int y=0; y<10; y++)
+ for (int x=0; x<10; x++)
+ rt.addshape(new Sphere(Vector3(x*2-10, (float)random()/RAND_MAX*5.0, y*2-10), 0.45, &mat_sph));
rt.setCamera(&cam);
cam.setEye(Vector3(0,0,10));
/* build kd-tree */
+ top.setMaxDepth(100);
top.optimize();
/* loop... */
--- a/demos/spheres_ao.py Thu Nov 29 18:30:16 2007 +0100
+++ b/demos/spheres_ao.py Fri Nov 30 00:44:51 2007 +0100
@@ -7,7 +7,7 @@
import Image
rt = Raytracer()
-rt.ambientocclusion(samples=60, distance=6.0, angle=0.5)
+rt.ambientocclusion(samples=100, distance=16.0, angle=0.5)
light1 = Light(position=(0.0, 5.0, -5.0), colour=(0.7, 0.3, 0.6))
light1.castshadows(False)
--- a/src/kdtree.cc Thu Nov 29 18:30:16 2007 +0100
+++ b/src/kdtree.cc Fri Nov 30 00:44:51 2007 +0100
@@ -50,11 +50,14 @@
};
// stack element for kd-tree traversal
-struct StackElem
+class StackElem
{
+public:
KdNode* node; /* pointer to far child */
float t; /* the entry/exit signed distance */
Vector3 pb; /* the coordinates of entry/exit point */
+ StackElem(KdNode *anode, const float &at, const Vector3 &apb):
+ node(anode), t(at), pb(apb) {};
};
// ----------------------------------------
@@ -96,9 +99,9 @@
delete[] children;
}
-void KdNode::subdivide(BBox bbox, int depth)
+void KdNode::subdivide(BBox bbox, int maxdepth)
{
- if (depth >= 20 || shapes->size() <= 4)
+ if (maxdepth <= 0 || shapes->size() <= 2)
{
setLeaf();
return;
@@ -295,8 +298,8 @@
lbb.H.cell[axis] = split;
rbb.L.cell[axis] = split;
- children[0].subdivide(lbb, depth+1);
- children[1].subdivide(rbb, depth+1);
+ children[0].subdivide(lbb, maxdepth-1);
+ children[1].subdivide(rbb, maxdepth-1);
}
void KdTree::build()
@@ -306,7 +309,7 @@
ShapeList::iterator shape;
for (shape = shapes.begin(); shape != shapes.end(); shape++)
root->addShape(*shape);
- root->subdivide(bbox, 0);
+ root->subdivide(bbox, max_depth);
built = true;
}
@@ -328,29 +331,23 @@
KdNode *farchild, *node;
node = root; /* start from the kd-tree root node */
- stack<StackElem*> st;
-
- StackElem *enPt = new StackElem();
- enPt->t = a; /* set the signed distance */
- enPt->node = NULL;
+ /* std vector is much faster than stack */
+ vector<StackElem*> st;
- /* distinguish between internal and external origin */
- if (a >= 0.0) /* a ray with external origin */
- enPt->pb = ray.o + ray.dir * a;
- else /* a ray with internal origin */
- enPt->pb = ray.o;
+ StackElem *enPt = new StackElem(NULL, a,
+ /* distinguish between internal and external origin of a ray*/
+ a >= 0.0 ?
+ ray.o + ray.dir * a : /* external */
+ ray.o); /* internal */
/* setup initial exit point in the stack */
- StackElem *exPt = new StackElem();
- exPt->t = b;
- exPt->pb = ray.o + ray.dir * b;
- exPt->node = NULL; /* set termination flag */
- st.push(exPt);
+ StackElem *exPt = new StackElem(NULL, b, ray.o + ray.dir * b);
+ st.push_back(exPt);
/* loop, traverse through the whole kd-tree, until an object is intersected or ray leaves the scene */
while (node)
{
- exPt = st.top();
+ exPt = st.back();
/* loop until a leaf is found */
while (!node->isLeaf())
{
@@ -393,13 +390,11 @@
t = (splitVal - ray.o.cell[axis]) / ray.dir.cell[axis];
/* setup the new exit point and push it onto stack */
- exPt = new StackElem();
- exPt->t = t;
- exPt->node = farchild;
+ exPt = new StackElem(farchild, t, Vector3());
exPt->pb[axis] = splitVal;
exPt->pb[(axis+1)%3] = ray.o.cell[(axis+1)%3] + t * ray.dir.cell[(axis+1)%3];
exPt->pb[(axis+2)%3] = ray.o.cell[(axis+2)%3] + t * ray.dir.cell[(axis+2)%3];
- st.push(exPt);
+ st.push_back(exPt);
} /* while */
/* current node is the leaf . . . empty or full */
@@ -419,8 +414,8 @@
{
while (!st.empty())
{
- delete st.top();
- st.pop();
+ delete st.back();
+ st.pop_back();
}
nearest_distance = dist;
return nearest_shape;
@@ -430,7 +425,7 @@
/* retrieve the pointer to the next node, it is possible that ray traversal terminates */
node = enPt->node;
- st.pop();
+ st.pop_back();
} /* while */
delete enPt;
--- a/src/kdtree.h Thu Nov 29 18:30:16 2007 +0100
+++ b/src/kdtree.h Fri Nov 30 00:44:51 2007 +0100
@@ -62,8 +62,9 @@
{
KdNode *root;
bool built;
+ int max_depth;
public:
- KdTree() : Container(), root(NULL), built(false) {};
+ KdTree() : Container(), root(NULL), built(false), max_depth(32) {};
~KdTree() { if (root) delete root; };
void addShape(Shape* aShape) { Container::addShape(aShape); built = false; };
Shape *nearest_intersection(const Shape *origin_shape, const Ray &ray,
@@ -72,6 +73,7 @@
void build();
void save(ostream &str, KdNode *node = NULL);
void load(istream &str, KdNode *node = NULL);
+ void setMaxDepth(int md) { max_depth = md; };
};
#endif
--- a/src/raytracer.cc Thu Nov 29 18:30:16 2007 +0100
+++ b/src/raytracer.cc Fri Nov 30 00:44:51 2007 +0100
@@ -109,7 +109,7 @@
float L_dot_N = dot(L, normal);
if (L_dot_N > 0) {
// test if this light is occluded (sharp shadows)
- if ((*light)->shadows) {
+ if ((*light)->cast_shadows) {
Ray shadow_ray = Ray(P, L);
float dist = FLT_MAX;
if (top->nearest_intersection(nearest_shape, shadow_ray, dist))
@@ -124,9 +124,8 @@
}
// 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) {
+ if (depth < 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;
@@ -160,8 +159,11 @@
static void *renderrow(void *data)
{
RenderrowData *d = (RenderrowData*) data;
+ int subsample = d->rt->getSubsample();
+ float subsample2 = 1.0/(subsample*subsample);
+ int ww = d->w*3;
Vector3 dir = d->dfix;
- for (int x = 0; x < d->w; x++) {
+ for (int x = 0; x < d->w; x += subsample) {
// generate a ray from eye passing through this pixel
#if OVERSAMPLING
// 5x oversampling
@@ -182,11 +184,117 @@
dir.normalize();
Ray ray(d->eye, dir);
Colour c = d->rt->raytrace(ray, 0, NULL);
+ if (subsample > 1)
+ {
+ Colour ic;
+ // top-left is 'c'
+ // top-right
+ Vector3 tmpdir = dir + (subsample-1)*d->dx;
+ tmpdir.normalize();
+ Ray ray(d->eye, tmpdir);
+ Colour c2 = d->rt->raytrace(ray, 0, NULL);
+ // bottom right
+ tmpdir += (subsample-1)*d->dy;
+ tmpdir.normalize();
+ ray.dir = tmpdir;
+ Colour c4 = d->rt->raytrace(ray, 0, NULL);
+ // bottom left
+ tmpdir = dir + (subsample-1)*d->dy;
+ tmpdir.normalize();
+ ray.dir = tmpdir;
+ Colour c3 = d->rt->raytrace(ray, 0, NULL);
+ // are the colors similar?
+ float m = (c-c2).mag2();
+ m = max(m, (c2-c3).mag2());
+ m = max(m, (c3-c4).mag2());
+ m = max(m, (c4-c).mag2());
+ if (m < 0.001)
+ {
+ // interpolate
+ float *i = d->iter;
+ for (int x = 0; x < subsample; x++)
+ {
+ for (int y = 0; y < subsample; y++)
+ {
+ ic = c*(subsample-x)*(subsample-y)*subsample2
+ + c2*(x)*(subsample-y)*subsample2
+ + c3*(subsample-x)*(y)*subsample2
+ + c4*(x)*(y)*subsample2;
+ *(i + ww*y) = ic.r;
+ *(i + ww*y + 1) = ic.g;
+ *(i + ww*y + 2) = ic.b;
+ }
+ i += 3;
+ }
+ d->iter = i;
+ }
+ else
+ {
+ // render
+ Vector3 tmpdir = dir;
+ // first column
+ *(d->iter) = c.r;
+ *(d->iter + 1) = c.g;
+ *(d->iter + 2) = c.b;
+ for (int y = 1; y < subsample-1; y++)
+ {
+ Vector3 tmp2dir = tmpdir + y*d->dy;
+ tmp2dir.normalize();
+ ray.dir = tmp2dir;
+ ic = d->rt->raytrace(ray, 0, NULL);
+ *(d->iter + ww*y) = ic.r;
+ *(d->iter + ww*y + 1) = ic.g;
+ *(d->iter + ww*y + 2) = ic.b;
+ }
+ *(d->iter + ww*(subsample-1)) = c3.r;
+ *(d->iter + ww*(subsample-1) + 1) = c3.g;
+ *(d->iter + ww*(subsample-1) + 2) = c3.b;
+ d->iter += 3;
+ tmpdir += d->dx;
+ // middle
+ for (int x = 1; x < subsample-1; x++)
+ {
+ for (int y = 0; y < subsample; y++)
+ {
+ Vector3 tmp2dir = tmpdir + y*d->dy;
+ tmp2dir.normalize();
+ ray.dir = tmp2dir;
+ ic = d->rt->raytrace(ray, 0, NULL);
+ *(d->iter + ww*y) = ic.r;
+ *(d->iter + ww*y + 1) = ic.g;
+ *(d->iter + ww*y + 2) = ic.b;
+ }
+ d->iter += 3;
+ tmpdir += d->dx;
+ }
+ // last column
+ *(d->iter) = c2.r;
+ *(d->iter + 1) = c2.g;
+ *(d->iter + 2) = c2.b;
+ for (int y = 1; y < subsample-1; y++)
+ {
+ Vector3 tmp2dir = tmpdir + y*d->dy;
+ tmp2dir.normalize();
+ ray.dir = tmp2dir;
+ ic = d->rt->raytrace(ray, 0, NULL);
+ *(d->iter + ww*y) = ic.r;
+ *(d->iter + ww*y + 1) = ic.g;
+ *(d->iter + ww*y + 2) = ic.b;
+ }
+ *(d->iter + ww*(subsample-1)) = c4.r;
+ *(d->iter + ww*(subsample-1) + 1) = c4.g;
+ *(d->iter + ww*(subsample-1) + 2) = c4.b;
+ d->iter += 3;
+ }
+ }
#endif
- *(d->iter++) = c.r;
- *d->iter++ = c.g;
- *d->iter++ = c.b;
- dir += d->dx;
+ if (subsample <= 1)
+ {
+ *d->iter++ = c.r;
+ *d->iter++ = c.g;
+ *d->iter++ = c.b;
+ }
+ dir += d->dx*subsample;
}
#ifdef PTHREADS
pthread_exit((void *)d);
@@ -217,7 +325,7 @@
/* for each pixel... */
infomsg("* rendering row 0 ( 0%% done)");
- for (int y = 0; y < h; y++)
+ for (int y = 0; y < h; y += subsample)
{
d = (RenderrowData*) malloc(sizeof(RenderrowData));
d->rt = this;
@@ -225,9 +333,7 @@
d->eye = camera->eye;
d->dfix = dfix;
d->dx = dx;
-#if OVERSAMPLING
d->dy = dy;
-#endif
d->iter = buffer + y*3*w;
#ifdef PTHREADS
/* create new thread and increase 't' */
@@ -248,7 +354,7 @@
renderrow((void *)d);
free(d);
#endif
- dfix += dy;
+ dfix += dy*subsample;
infomsg("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b%4d (%2d%% done)", y, y*100/(h-1));
}
infomsg("\n");
--- a/src/raytracer.h Thu Nov 29 18:30:16 2007 +0100
+++ b/src/raytracer.h Fri Nov 30 00:44:51 2007 +0100
@@ -20,10 +20,7 @@
struct RenderrowData {
Raytracer *rt;
int w;
- Vector3 eye, dfix, dx;
-#if OVERSAMPLING
- Vector3 dy;
-#endif
+ Vector3 eye, dfix, dx, dy;
float *iter;
};
@@ -36,11 +33,13 @@
int ao_samples;
float ao_distance, ao_angle;
int num_threads;
+ int subsample;
+ int max_depth;
Vector3 SphereDistribute(int i, int n, float extent, Vector3 &normal);
public:
Raytracer(): top(NULL), camera(NULL), lights(), bg_colour(0.0, 0.0, 0.0),
- ao_samples(0), num_threads(4) {};
+ ao_samples(0), num_threads(4), subsample(8), max_depth(4) {};
void render(int w, int h, float *buffer);
Colour raytrace(Ray &ray, int depth, Shape *origin_shape);
void addshape(Shape *shape) { top->addShape(shape); };
@@ -48,6 +47,8 @@
void setCamera(Camera *cam) { camera = cam; };
void setTop(Container *atop) { top = atop; };
Container *getTop() { return top; };
+ int getSubsample() { return subsample; };
+ void setMaxDepth(int newdepth) { max_depth = newdepth; };
void ambientocclusion(int samples, float distance, float angle);
void setThreads(int num) { num_threads = num; };
--- a/src/raytracermodule.cc Thu Nov 29 18:30:16 2007 +0100
+++ b/src/raytracermodule.cc Fri Nov 30 00:44:51 2007 +0100
@@ -88,7 +88,7 @@
if (!PyArg_ParseTuple(args, "i", &shadows))
return NULL;
- ((LightObject *)self)->light->castshadows(shadows);
+ ((LightObject *)self)->light->castShadows(shadows);
Py_INCREF(Py_None);
return Py_None;
--- a/src/scene.cc Thu Nov 29 18:30:16 2007 +0100
+++ b/src/scene.cc Fri Nov 30 00:44:51 2007 +0100
@@ -12,6 +12,8 @@
void Camera::rotate(const Quaternion &q)
{
+ /*
+ //non-optimized
Quaternion res;
res = q * Quaternion(u) * conjugate(q);
u = res.toVector();
@@ -19,10 +21,9 @@
v = res.toVector();
res = q * Quaternion(p) * conjugate(q);
p = res.toVector();
- p.normalize();
- u.normalize();
- v.normalize();
-/* // optimized version
+ */
+
+ // optimized
float t2 = q.a*q.b;
float t3 = q.a*q.c;
float t4 = q.a*q.d;
@@ -44,7 +45,10 @@
x = 2*( (t8 + t10)*v.x + (t6 - t4)*v.y + (t3 + t7)*v.z ) + v.x;
y = 2*( (t4 + t6)*v.x + (t5 + t10)*v.y + (t9 - t2)*v.z ) + v.y;
z = 2*( (t7 - t3)*v.x + (t2 + t9)*v.y + (t5 + t8)*v.z ) + v.z;
- v = Vector3(x,y,z);*/
+ v = Vector3(x,y,z);
+ p.normalize();
+ u.normalize();
+ v.normalize();
}
void Camera::move(const float fw, const float left, const float up)
@@ -92,28 +96,15 @@
bool Sphere::intersect(const Ray &ray, float &dist)
{
- Vector3 V = ((Ray)ray).o - center;
-
- float Vd = - dot(V, ray.dir);
- float Det = Vd * Vd - (dot(V,V) - sqr_radius);
-
+ Vector3 V = ray.o - center;
+ register float d = -dot(V, ray.dir);
+ register float Det = d * d - (dot(V,V) - sqr_radius);
if (Det > 0) {
- Det = sqrtf(Det);
- float t1 = Vd - Det;
- if (t1 > 0)
+ d -= sqrtf(Det);
+ if (d > 0 && d < dist)
{
- if (t1 < dist) {
- dist = t1;
- return true;
- }
- } else {
- float t2 = Vd + Det;
- if (t2 > 0)
- {
- // ray from inside of the sphere
- dist = t2;
- return true;
- }
+ dist = d;
+ return true;
}
}
return false;
@@ -163,8 +154,15 @@
bool Box::intersect(const Ray &ray, float &dist)
{
- float b;
- return get_bbox().intersect(ray, dist, b);
+ float a,b;
+ bool res = get_bbox().intersect(ray, a, b);
+ if (res && a < dist)
+ {
+ dist = a;
+ return true;
+ }
+ else
+ return false;
}
Vector3 Box::normal(Vector3 &P)
--- a/src/scene.h Thu Nov 29 18:30:16 2007 +0100
+++ b/src/scene.h Fri Nov 30 00:44:51 2007 +0100
@@ -92,11 +92,11 @@
public:
Vector3 pos;
Colour colour;
- int shadows;
+ bool cast_shadows;
Light(const Vector3 &position, const Colour &acolour):
- pos(position), colour(acolour), shadows(1) {};
- void castshadows(bool ashadows) { shadows = ashadows; };
+ pos(position), colour(acolour), cast_shadows(true) {};
+ void castShadows(bool cast) { cast_shadows = cast; };
};
class Texture
--- a/src/vector.h Thu Nov 29 18:30:16 2007 +0100
+++ b/src/vector.h Fri Nov 30 00:44:51 2007 +0100
@@ -34,6 +34,8 @@
// index operator
float &operator[](int index) { return cell[index]; };
+ bool operator==(Vector3 &v) { return x==v.x && y==v.y && z==v.z; };
+
// normalize
Vector3 normalize()
{