charles/src/scene.cc

293 lines
5.9 KiB
C++
Raw Normal View History

2013-09-10 16:28:32 -07:00
/* scene.c
*
* Definition of Scene-related functions.
*
* Eryn Wells <eryn@erynwells.me>
*/
2013-09-12 09:10:22 -07:00
#include <chrono>
2013-09-10 16:28:32 -07:00
#include <cmath>
2013-09-10 21:04:56 -07:00
#include <cstdio>
2013-09-10 16:28:32 -07:00
#include "basics.h"
#include "light.h"
2013-09-10 16:28:32 -07:00
#include "object.h"
#include "scene.h"
2013-09-10 21:04:56 -07:00
#include "writer.h"
2013-09-10 16:28:32 -07:00
Scene::Scene()
2013-09-10 21:04:56 -07:00
: width(640), height(480),
camera(new PerspectiveCamera()),
2013-09-11 10:33:16 -07:00
max_depth(5),
2013-09-21 17:01:11 -07:00
min_weight(1e-4),
2013-09-13 18:25:09 -07:00
ambient(new AmbientLight()),
2013-09-13 14:15:55 -07:00
shapes(),
lights(),
2013-09-12 09:10:22 -07:00
nrays(0),
2013-09-10 16:28:32 -07:00
pixels(NULL)
{ }
Scene::~Scene()
{
if (camera) {
delete camera;
camera = NULL;
}
2013-09-13 14:15:55 -07:00
if (ambient != NULL) {
delete ambient;
}
for (Shape *s : shapes) {
delete s;
}
shapes.clear();
for (PointLight *l : lights) {
delete l;
}
lights.clear();
2013-09-10 16:28:32 -07:00
if (pixels != NULL) {
delete[] pixels;
_is_rendered = false;
}
}
bool
Scene::is_rendered()
const
{
return _is_rendered;
}
2013-09-10 21:04:56 -07:00
int
Scene::get_width()
const
{
return width;
}
int
Scene::get_height()
const
{
return height;
}
2013-09-13 18:25:09 -07:00
AmbientLight &
Scene::get_ambient()
const
{
return *ambient;
}
2013-09-10 21:04:56 -07:00
const Color *
Scene::get_pixels()
const
{
return pixels;
}
2013-09-10 16:28:32 -07:00
/*
* scene_load --
*
* Load scene objects into this Scene from the given file.
*/
void
2013-09-10 21:04:56 -07:00
Scene::read(const std::string &filename)
2013-09-10 16:28:32 -07:00
{ }
/*
* scene_save --
*
* Write a rendered scene to the given file.
*/
void
2013-09-10 21:04:56 -07:00
Scene::write(Writer &writer, const std::string &filename)
{
writer.write_scene(*this, filename);
}
2013-09-10 16:28:32 -07:00
/*
* Scene::render --
*
* Render the given Scene.
*/
void
Scene::render()
{
2013-09-12 09:10:22 -07:00
std::chrono::time_point<std::chrono::system_clock> start, end;
start = std::chrono::system_clock::now();
2013-09-10 16:28:32 -07:00
pixels = new Color[width * height];
Ray primary_ray;
Vector3 o, d;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
primary_ray = camera->compute_primary_ray(x, width, y, height);
Color c = trace_ray(primary_ray);
2013-09-10 21:04:56 -07:00
pixels[y * width + x] = c;
2013-09-10 16:28:32 -07:00
}
}
2013-09-12 09:10:22 -07:00
end = std::chrono::system_clock::now();
std::chrono::duration<float> seconds = end - start;
2013-09-10 16:28:32 -07:00
_is_rendered = true;
2013-09-12 09:10:22 -07:00
printf("Scene rendered. %d rays traced in %f seconds.\n", nrays, seconds.count());
2013-09-10 16:28:32 -07:00
}
2013-09-10 21:49:01 -07:00
/*
* Scene::add_shape --
*
* Add a shape to the scene.
*/
2013-09-10 16:28:32 -07:00
void
Scene::add_shape(Shape *shape)
{
shapes.push_back(shape);
}
2013-09-10 21:49:01 -07:00
/*
* Scene::add_light --
*
* Add a light to the scene.
*/
void
Scene::add_light(PointLight *light)
2013-09-10 21:49:01 -07:00
{
lights.push_back(light);
}
/*
* Scene::trace_ray --
*
* Trace the given ray through the scene, recursing until depth has been reached.
*/
2013-09-10 16:28:32 -07:00
Color
Scene::trace_ray(const Ray &ray,
const int depth,
const float weight)
2013-09-10 16:28:32 -07:00
{
2013-09-21 17:01:11 -07:00
if (depth >= max_depth || weight <= min_weight) {
2013-09-21 15:42:11 -07:00
return Color::Black;
}
Color out_color = Color::Black;
2013-09-10 16:28:32 -07:00
Shape *intersected_shape = NULL;
float *t = NULL;
float nearest_t = INFINITY;
int nints;
2013-09-12 09:10:22 -07:00
// Keep stats.
nrays++;
// Find intersections of this ray with objects in the scene.
2013-09-10 16:28:32 -07:00
for (Shape *s : shapes) {
nints = s->does_intersect(ray, &t);
if (nints > 0) {
for (int i = 0; i < nints; i++) {
if (t[i] < 1e-2) {
break;
}
2013-09-10 16:28:32 -07:00
if (t[i] < nearest_t) {
intersected_shape = s;
nearest_t = t[i];
}
}
delete[] t;
}
}
// If there was no intersection, return black.
if (intersected_shape == NULL) {
return out_color;
2013-09-10 16:28:32 -07:00
}
Material* shape_material = intersected_shape->get_material();
Color shape_color = shape_material->get_diffuse_color();
2013-09-13 18:56:07 -07:00
2013-09-11 22:03:50 -07:00
Vector3 intersection = ray.parameterize(nearest_t);
Vector3 normal = intersected_shape->compute_normal(intersection);
/*
* Diffuse lighting. (Shading, etc.)
*/
2013-09-13 18:56:07 -07:00
Vector3 light_direction;
float ldotn, diffuse_level, ambient_level;
Ray shadow_ray;
2013-09-11 22:03:50 -07:00
for (PointLight *l : lights) {
2013-09-13 18:56:07 -07:00
light_direction = (intersection - l->get_origin()).normalize();
ldotn = light_direction.dot(normal);
if (ldotn < 0) {
ldotn = 0.0;
}
2013-09-13 18:56:07 -07:00
diffuse_level = shape_material->get_diffuse_level();
2013-09-13 18:56:07 -07:00
ambient_level = 1.0 - diffuse_level;
shadow_ray = Ray(intersection, light_direction);
for (Shape *s : shapes) {
// Skip the intersected shape.
if (s == intersected_shape) {
continue;
}
// Figure out if we're in shadow.
if (s->does_intersect(shadow_ray, NULL) > 0) {
diffuse_level = 0.0;
break;
}
}
2013-09-21 17:06:49 -07:00
/*
* Compute basic Lambert diffuse shading for this object.
*/
2013-09-13 18:56:07 -07:00
out_color += shape_color * ( ambient_level * ambient->compute_color_contribution()
+ diffuse_level * ldotn);
2013-09-11 22:03:50 -07:00
}
2013-09-21 15:42:11 -07:00
/*
* Specular lighting. (Reflections, etc.)
*/
float specular_level = shape_material->get_specular_level();
const Color &specular_color = shape_material->get_specular_color();
/*
* Compute the reflection ray. Computing the direction of the reflection ray is done by the following formula:
*
* d = dr - 2n(dr . n)
*
* where d is the direction, dr is the direction of the incoming ray, and n is the normal vector. Period (.)
* indicates the dot product.
*
* The origin of the reflection ray is the point on the surface where the incoming ray intersected with it.
*/
Ray reflection_ray = Ray(intersection, ray.direction - 2.0 * normal * ray.direction.dot(normal));
Color reflection_color = trace_ray(reflection_ray, depth + 1, weight * specular_level);
// TODO: Mix in specular_color of material.
out_color += specular_level * specular_color * reflection_color;
2013-09-11 22:03:50 -07:00
return out_color;
2013-09-10 16:28:32 -07:00
}