If you count blanks and comments, it's exactly 381 for 100% of the ray tracer itself, including image loading/saving, pixel access, ray calculation, and all that:
Code: Select all
#include <iostream>
#include <string>
#include <list>
#include <cstdlib>
#include <numeric>
#include <ctime>
#include <vector>
#include <algorithm>
#include <ctime>
#include <cmath>
#include <boost/shared_ptr.hpp>
#include <boost/foreach.hpp>
#include <boost/optional.hpp>
#include <boost/any.hpp>
//#include <boost/thread.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/thread.hpp>
#include <corona.h>
#include <cml/cml.h>
#include "phys/body.hpp"
#include "phys/ray.hpp"
#include "phys/world.hpp"
#include "phys/mesh.hpp"
#include "types.hpp"
#include "rand.hpp"
using namespace engine;
class entity: public phys::body
{
private:
public:
entity(const phys::world &w):
phys::body(w)
{
misc = this;
}
struct material_t
{
float reflectivity;
vec3 diffuse, specular;
material_t(): reflectivity(0), diffuse(1, 1, 1), specular(1, 1, 1) {}
} material;
};
struct light
{
vec3 diffuse;
vec3 specular;
vec3 position;
float radius;
light(const vec3 &d = vec3(1, 1, 1), const vec3 &s = vec3(1, 1, 1), const vec3 &p = vec3(0, 0, 0), float r = 10):
diffuse(d), specular(s), position(p), radius(r) {}
};
typedef boost::shared_ptr<entity> entityPtr;
typedef std::list<entityPtr> entityList;
typedef std::list<light> lightList;
mat4 lookAt(const vec3 &p, const vec3 &dir, const vec3 &up)
{
mat4 ret;
cml::matrix_look_at_RH(ret, p, dir, up);
return ret;
}
struct ray_t
{
vec3 origin, dir;
ray_t(const vec3 &o, const vec3 &d):
origin(o), dir(d) {}
ray_t():
origin(0, 0, 0), dir(0, 0, -1) {}
};
ray_t screenRay(float x, float y, const mat4 &camMat, const float FOCAL_DIST = 1) // normalized screen coords
{
x = x * 2 - 1;
y = y * 2 - 1;
vec3 orig(camMat(0, 3), camMat(1, 3), camMat(2, 3));
vec4 dest(x, y, -FOCAL_DIST, 1);
//orig = mat * orig;
dest = camMat * dest;
return ray_t(orig, normalize(vec3(dest[0], dest[1], dest[2]) - orig));
}
//vec3 calcDirect(vec3 p, vec3 n, const light &l, const entity::material_t &mat)
//{
// vec3 distv3 = l.position - p;
// float dist = length(distv3);
// distv3 /= dist; // normalize
//
// float att = 1.0 - saturate(dist / l.radius);
//
// vec3 illum(0, 0, 0);
// { // NdotL
// float NdotL = saturate(dot(n, distv3));
// illum += mul(mat.diffuse, l.diffuse) * NdotL * att;
// }
// { // specular
// }
// return illum;
//}
boost::optional<phys::ray::hitInfo> rayTrace(const ray_t &start, const phys::world &w, float dist = 1.e3)
{
phys::ray ray(w);
if (ray(start.origin, start.origin + start.dir * dist))
return ray.result;
return boost::optional<phys::ray::hitInfo>();
}
size_t maxIterations = 10;
boost::optional<vec3> colourFromRay(const ray_t &start, const phys::world &w, const mat4 &invViewMat,
const lightList &lights, size_t iter = 0)
{
if (iter > maxIterations)
return boost::optional<vec3>();
vec3 cameraPos(invViewMat(0, 3), invViewMat(1, 3), invViewMat(2, 3));
boost::optional<phys::ray::hitInfo> ret = rayTrace(start, w);
if (ret)
{
phys::ray::hitInfo info = ret.get();
entity *ent = boost::any_cast<entity*>(info.b->misc);
vec3 eyePos(invViewMat(0, 3), invViewMat(1, 3), invViewMat(2, 3));
vec3 eyeDir = normalize(eyePos - info.p);
vec3 illum(0, 0, 0);
BOOST_FOREACH(const light &l, lights)
{
vec3 distv3 = l.position - info.p;
// raw NdotL, just to figure out whether we should compute lighting
float NdotLRaw = dot(info.n, distv3);
if (NdotLRaw > 0)
{
float dist = length(distv3);
distv3 /= dist; // normalize
// take 'real' NdotL
float NdotL = dot(info.n, distv3);
float att = 1.0 - saturate(dist / l.radius);
vec3 diffuse = mul(ent->material.diffuse, l.diffuse) * NdotL * att;
vec3 specular(0, 0, 0);
vec3 hv = normalize(distv3 + eyeDir);
float NdotH = dot(info.n, hv);
if (NdotH > 0)
{
specular = /*mul(ent->material.specular, l.specular)*/vec3(1, 1, 1) * std::pow(NdotH, 64);
}
ray_t shadowRay(info.p, normalize(l.position - info.p));
illum += (diffuse + specular) * !rayTrace(shadowRay, w, l.radius);
}
}
if (ent->material.reflectivity > 0.01)
{
boost::optional<vec3> c = colourFromRay(ray_t(info.p, reflect(start.dir, info.n)), w, invViewMat, lights, ++iter);
if (c)
illum += mul(ent->material.specular, c.get()) * ent->material.reflectivity;
}
return illum;
}
float sky = /*std::abs(*/dot(start.dir, vec3(0, 1, 0))/*)*/;
if (sky <= 0)
return boost::optional<vec3>();
return boost::optional<vec3>(vec3(223.0 / 255.0, 151.0 / 255.0, 0) * sky);
}
void createScene(entityList &scene, phys::world &world, size_t numObjects = 250)
{
for (size_t i = 0; i < numObjects; ++i)
{
entityPtr ent(new entity(world));
vec3 p(randUnit() * 25, rand01() * 50, randUnit() * 25);
vec3 d(10, 10, 10);
ent->collision(phys::mesh::ball(world, d));
ent->pos(p);
ent->mass(10);
entity::material_t &m = ent->material;
m.reflectivity = 1;
m.diffuse = m.specular = randUnitVec() * 0.5 + vec3(0.5, 0.5, 0.5);
//m.specular = randUnitVec() * 0.5 + vec3(0.5, 0.5, 0.5);
scene.push_back(ent);
}
// ent
{
entityPtr ent(new entity(world));
vec3 d(150, 1, 150);
ent->collision(phys::mesh::box(world, d));
ent->pos(vec3(0, d[1] * -0.5, 0));
ent->mass(0);
ent->material.reflectivity = 1;
ent->material.diffuse = ent->material.specular = vec3(1, 1, 1);
scene.push_back(ent);
}
}
void createLights(lightList &lights)
{
lights.push_back(light(vec3(1, 1, 1), vec3(1, 1, 1), vec3(0, 50, 0), 100));
//lights.push_back(light(vec3(1, 1, 1), vec3(1, 1, 1), vec3(0, 15, 0), 25));
//lights.push_back(light(vec3(1, 1, 1), vec3(1, 1, 1), vec3(-50, 75, 50), 150));
//lights.push_back(light(vec3(1, 1, 1), vec3(1, 1, 1), vec3(-50, 75, -50), 150));
//lights.push_back(light(vec3(1, 1, 1), vec3(1, 1, 1), vec3(50, 75, 50), 150));
//lights.push_back(light(vec3(1, 1, 1), vec3(1, 1, 1), vec3(50, 75, -50), 150));
}
mat4 createViewMat()
{
return lookAt(vec3(25, 25, 25), vec3(0, 0, 0), vec3(0, 1, 0));
}
struct pixel
{
float x, y;
engine::byte *p;
pixel(float x, float y, engine::byte *p):
x(x), y(y), p(p) {}
};
void tracePixel(const pixel &p, const mat4 &viewMat, const mat4 &invViewMat, const phys::world &w, const lightList &lights)
{
// y goes from bottom to top
ray_t r = screenRay(p.x, p.y, invViewMat);
vec3 c = saturate(colourFromRay(r, w, invViewMat, lights).get_value_or(vec3(0, 0, 0)));
for (size_t j = 0; j < 3; ++j)
p.p[j] = engine::byte(c[j] * 255.0);
p.p[3] = 255;
}
void tracePixels(const std::vector<pixel> &pixels, const mat4 &viewMat, const mat4 &invViewMat,
const phys::world &w, const lightList &lights)
{
for (size_t i = 0; i < pixels.size(); ++i)
tracePixel(pixels[i], viewMat, invViewMat, w, lights);
}
struct tracer
{
std::vector<pixel> pixels;
mat4 viewMat, invViewMat;
const phys::world &world;
const lightList &lights;
tracer(const std::vector<pixel> &pixels, const mat4 &viewMat, const mat4 &invViewMat,
const phys::world &w, const lightList &lights):
pixels(pixels), viewMat(viewMat), invViewMat(invViewMat), world(w), lights(lights) {}
inline void operator()() { tracePixels(pixels, viewMat, invViewMat, world, lights); }
};
typedef boost::shared_ptr<corona::Image> imagePtr;
inline imagePtr create(size_t w, size_t h) { return imagePtr(corona::CreateImage(w, h, corona::PF_R8G8B8A8)); }
inline void save(const imagePtr &img, size_t i = 0)
{
corona::SaveImage(("out" + boost::lexical_cast<engine::string>(i) + ".png").c_str(), corona::FF_AUTODETECT, img.get());
}
void go(const mat4 &viewMat, const mat4 &invViewMat,
const phys::world &world, const lightList &lights, std::vector<pixel> &pixels, size_t numThreads)
{
size_t numPixels = pixels.size();
size_t pixelsPerThread = numPixels / numThreads;
std::vector<std::vector<pixel> > threadPixels(numThreads);
for (size_t i = 0; i < threadPixels.size(); ++i)
{
std::vector<pixel> &v = threadPixels[i];
v = std::vector<pixel>(pixels.begin() + i * pixelsPerThread, pixels.begin() + (i + 1) * pixelsPerThread);
}
typedef boost::shared_ptr<boost::thread> threadPtr;
std::vector<threadPtr > threads;
for (size_t i = 1; i < threadPixels.size(); ++i)
threads.push_back(threadPtr(new boost::thread(tracer(threadPixels[i], viewMat, invViewMat, world, lights))));
// use the current thread, too
tracePixels(threadPixels[0], viewMat, invViewMat, world, lights);
// wait for other threads
for (size_t i = 0; i < threads.size(); ++i)
threads[i]->join();
}
int main(int argc, char **argv)
{
seedRand();
size_t width = 512, height = 512, numThreads = 1;
std::cout << "size: ";
std::cin >> width;
if (width < 4)
width = 4;
height = width;
std::cout << "number of threads: ";
std::cin >> numThreads;
std::cout << "number of frames: ";
size_t numFrames = 1;
std::cin >> numFrames;
std::cout << "delta time (seconds) (big if you're not rendering loads of frames): ";
double deltaTime = 0.25;
std::cin >> deltaTime;
std::cout << "number of objects (phys time is pretty low usually): ";
size_t numObjects = 250;
std::cin >> numObjects;
std::cout << "max number of reflections: ";
std::cin >> maxIterations;
imagePtr screen = create(width, height);
engine::byte *data = (engine::byte*)screen->getPixels();
std::vector<pixel> pixels;
pixels.reserve(width * height);
for (size_t i = 0; i < pixels.capacity(); ++i)
{
size_t x = i % width;
size_t y = i / width;
pixels.push_back(pixel(float(x) / width, 1.0 - float(y) / height, data + i * 4));
}
mat4 viewMat = createViewMat();
mat4 invViewMat = inverse(viewMat);
phys::world world;
world.threads(numThreads);
entityList scene;
createScene(scene, world, numObjects);
lightList lights;
createLights(lights);
for (size_t i = 0; i < numFrames; ++i)
{
go(viewMat, invViewMat, world, lights, pixels, numThreads);
world.tick(deltaTime);
save(screen, i);
std::cout << "done with frame " << i << "\n";
}
return 0;
}