naive color driven sub-sampling pyrit
authorRadek Brich <radek.brich@devl.cz>
Fri, 30 Nov 2007 00:44:51 +0100
branchpyrit
changeset 21 79b516a3803d
parent 20 f22952603f29
child 22 76b7bd51d64a
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
TODO
ccdemos/realtime.cc
demos/spheres_ao.py
src/kdtree.cc
src/kdtree.h
src/raytracer.cc
src/raytracer.h
src/raytracermodule.cc
src/scene.cc
src/scene.h
src/vector.h
--- 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()
 	{