charles/src/scene.cc
2014-08-09 21:23:11 -07:00

397 lines
7.5 KiB
C++

/* scene.c
*
* Definition of Scene-related functions.
*
* Eryn Wells <eryn@erynwells.me>
*/
#include <cassert>
#include <cmath>
#include <cstdio>
#include <chrono>
#include "scene.hh"
#include "light.hh"
#include "log.hh"
#include "object.hh"
#include "writer.h"
#include "basics/basics.hh"
#define LOG_NAME "scene"
#include "logModule.hh"
using charles::basics::Color;
using charles::basics::Ray;
using charles::basics::Vector4;
namespace charles {
/*
* charles::Scene::Scene --
*/
Scene::Scene()
: mWidth(640),
mHeight(480),
mCamera(new PerspectiveCamera()),
mMaxDepth(5),
mMinWeight(1e-6),
mAmbient(Color(1, 1, 1), 0.2),
mObjects(),
mLights(),
mStats(),
mPixels(NULL)
{ }
/*
* charles::Scene::~Scene --
*/
Scene::~Scene()
{
mCamera.reset();
mObjects.clear();
for (PointLight *l : mLights) {
delete l;
}
mLights.clear();
if (mPixels != NULL) {
delete[] mPixels;
mIsRendered = false;
}
}
/*
* charles::Scene::IsRendered --
*/
bool
Scene::IsRendered()
const
{
return mIsRendered;
}
/*
* charles::Scene::GetWidth --
*/
UInt
Scene::GetWidth()
const
{
return mWidth;
}
/*
* charles::Scene::SetWidth --
*/
void
Scene::SetWidth(UInt w)
{
mWidth = w;
}
/*
* charles::Scene::GetHeight --
*/
UInt
Scene::GetHeight()
const
{
return mHeight;
}
/*
* charles::Scene::SetHeight --
*/
void
Scene::SetHeight(UInt h)
{
mHeight = h;
}
/*
* charles::Scene::GetCamera --
*/
Camera::Ptr
Scene::GetCamera()
const
{
return mCamera;
}
/*
* charles::Scene::SetCamera --
*/
void
Scene::SetCamera(Camera::Ptr camera)
{
mCamera = camera;
}
/*
* charles::Scene::GetAmbient --
*/
AmbientLight&
Scene::GetAmbient()
{
return mAmbient;
}
/*
* charles::Scene::GetPixels --
*/
const Color*
Scene::GetPixels()
const
{
return mPixels;
}
/*
* charles::Scene::Write --
*/
void
Scene::Write(Writer& writer,
const std::string& filename)
{
writer.write_scene(*this, filename);
}
/*
* charles::Scene::Render --
*/
void
Scene::Render()
{
LOG_INFO << "Rendering scene with " << mObjects.size() << " objects.";
printf("Rendering scene with %lu objects.\n", mObjects.size());
LogCamera();
LogObjects();
std::chrono::time_point<std::chrono::system_clock> start, end;
start = std::chrono::system_clock::now();
mPixels = new Color[mWidth * mHeight];
Ray primaryRay;
Vector4 o, d;
for (UInt y = 0; y < mHeight; y++) {
for (UInt x = 0; x < mWidth; x++) {
primaryRay = mCamera->PrimaryRay(x, mWidth, y, mHeight);
mStats.primaryRays++;
Color c = TraceRay(primaryRay);
mPixels[y * mWidth + x] = c;
}
}
end = std::chrono::system_clock::now();
std::chrono::duration<Double> seconds = end - start;
mIsRendered = true;
printf("Rendering completed in %f seconds.\n\n", seconds.count());
LOG_INFO << "Rendering completed in " << seconds.count() << " seconds.";
mStats.PrintRayTable();
printf("\n");
mStats.PrintIntersectionsTable();
}
/*
* charles::Scene::AddObject --
*/
void
Scene::AddObject(Object::Ptr obj)
{
mObjects.push_back(obj);
}
/*
* charles::Scene::AddLight --
*/
void
Scene::AddLight(PointLight* light)
{
mLights.push_back(light);
}
/*
* charles::Scene::TraceRay --
*/
Color
Scene::TraceRay(const Ray &ray,
const int depth,
const Double weight)
{
if (depth >= mMaxDepth || weight <= mMinWeight) {
return Color::Black;
}
Color outColor = Color::Black;
Object::Ptr intersectedObj;
TVector ts;
Double nearestT = INFINITY;
ts.reserve(2);
// Find intersections of this ray with objects in the scene.
for (Object::Ptr obj : mObjects) {
ts.clear();
if (obj->Intersect(ray, ts, mStats)) {
if (ts[0] < nearestT) {
intersectedObj = obj;
nearestT = ts[0];
}
}
}
// If there was no intersection, return black.
if (!intersectedObj) {
return outColor;
}
Material& material = intersectedObj->GetMaterial();
const Color& shapeColor = material.GetDiffuseColor();
Vector4 intersection = ray.Parameterize(nearestT);
Vector4 normal = intersectedObj->Normal(intersection);
/*
* Diffuse lighting. (Shading, etc.)
*/
Vector4 lightDirection;
Double ldotn, diffuseLevel, ambientLevel;
Ray shadowRay;
for (PointLight *l : mLights) {
lightDirection = basics::Normalized(l->GetOrigin() - intersection);
ldotn = lightDirection.Dot(normal);
/*
* TODO: What is this even for? Removing it makes the darker showers
* really bright.
*/
if (ldotn < 0) {
ldotn = 0.0;
}
diffuseLevel = material.GetDiffuseIntensity();
ambientLevel = 1.0 - diffuseLevel;
shadowRay = Ray(intersection, lightDirection);
for (Object::Ptr obj : mObjects) {
if (obj == intersectedObj) {
/* Skip the intersected shape. */
continue;
}
mStats.shadowRays++;
/* Figure out if we're in shadow. */
ts.clear();
if (obj->Intersect(shadowRay, ts, mStats)) {
diffuseLevel = 0.0;
break;
}
}
/*
* Compute basic Lambert diffuse shading for this object.
*/
outColor += shapeColor * ( ambientLevel * mAmbient.Contribution()
+ diffuseLevel * ldotn);
}
/*
* Specular lighting. (Reflections, etc.)
*/
#if 0
Double specular_level = shape_material.GetSpecularIntensity();
const Color& specular_color = shape_material.GetSpecularColor();
/*
* 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));
mStats.reflectionRays++;
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;
#endif
return outColor;
}
/*
* charles::Scene::LogCamera --
*/
void
Scene::LogCamera()
const
{
const Camera& c = *mCamera;
LOG_DEBUG << "BEGIN CAMERA";
LOG_DEBUG << " type = " << c.GetTypeString();
LOG_DEBUG << " origin = " << c.GetOrigin();
LOG_DEBUG << " direction = " << c.GetDirection();
LOG_DEBUG << " right = " << c.GetRight();
LOG_DEBUG << " up = " << c.GetUp();
LOG_DEBUG << " coordinate system is "
<< (c.IsLeftHanded() ? "left-handed" : "right-handed");
LOG_DEBUG << "END CAMERA";
}
/*
* charles::Scene::LogObjects --
*/
void
Scene::LogObjects()
const
{
LOG_DEBUG << "BEGIN SCENE OBJECTS";
for (Object::Ptr obj : mObjects) {
LOG_DEBUG << " " << *obj;
}
LOG_DEBUG << "END SCENE OBJECTS";
}
} /* namespace charles */