mitsuba/src/librender/scene.cpp

999 lines
32 KiB
C++

/*
This file is part of Mitsuba, a physically based rendering system.
Copyright (c) 2007-2012 by Wenzel Jakob and others.
Mitsuba is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License Version 3
as published by the Free Software Foundation.
Mitsuba is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mitsuba/render/scene.h>
#include <mitsuba/render/renderjob.h>
#include <mitsuba/core/plugin.h>
#include <mitsuba/core/statistics.h>
#define DEFAULT_BLOCKSIZE 32
MTS_NAMESPACE_BEGIN
// ===========================================================================
// Constructors, destructor and serialization-related code
// ===========================================================================
Scene::Scene()
: NetworkedObject(Properties()), m_blockSize(DEFAULT_BLOCKSIZE) {
m_kdtree = new ShapeKDTree();
m_sourceFile = new fs::path();
m_destinationFile = new fs::path();
}
Scene::Scene(const Properties &props)
: NetworkedObject(props), m_blockSize(DEFAULT_BLOCKSIZE) {
m_kdtree = new ShapeKDTree();
/* kd-tree construction: Enable primitive clipping? Generally leads to a
significant improvement of the resulting tree. */
if (props.hasProperty("kdClip"))
m_kdtree->setClip(props.getBoolean("kdClip"));
/* kd-tree construction: Relative cost of a triangle intersection operation
in the surface area heuristic. */
if (props.hasProperty("kdIntersectionCost"))
m_kdtree->setQueryCost(props.getFloat("kdIntersectionCost"));
/* kd-tree construction: Relative cost of a kd-tree traversal operation
in the surface area heuristic. */
if (props.hasProperty("kdTraversalCost"))
m_kdtree->setTraversalCost(props.getFloat("kdTraversalCost"));
/* kd-tree construction: Bonus factor for cutting away regions of empty space */
if (props.hasProperty("kdEmptySpaceBonus"))
m_kdtree->setEmptySpaceBonus(props.getFloat("kdEmptySpaceBonus"));
/* kd-tree construction: A kd-tree node containing this many or fewer
primitives will not be split */
if (props.hasProperty("kdStopPrims"))
m_kdtree->setStopPrims(props.getInteger("kdStopPrims"));
/* kd-tree construction: Maximum tree depth */
if (props.hasProperty("kdMaxDepth"))
m_kdtree->setMaxDepth(props.getInteger("kdMaxDepth"));
/* kd-tree construction: Specify the number of primitives, at which the
builder will switch from (approximate) Min-Max binning to the accurate
O(n log n) SAH-based optimization method. */
if (props.hasProperty("kdExactPrimitiveThreshold"))
m_kdtree->setExactPrimitiveThreshold(props.getInteger("kdExactPrimitiveThreshold"));
/* kd-tree construction: use multiple processors? */
if (props.hasProperty("kdParallelBuild"))
m_kdtree->setParallelBuild(props.getBoolean("kdParallelBuild"));
/* kd-tree construction: specify whether or not bad splits can be "retracted". */
if (props.hasProperty("kdRetract"))
m_kdtree->setRetract(props.getBoolean("kdRetract"));
/* kd-tree construction: Set the number of bad refines allowed to happen
in succession before a leaf node will be created.*/
if (props.hasProperty("kdMaxBadRefines"))
m_kdtree->setMaxBadRefines(props.getInteger("kdMaxBadRefines"));
m_sourceFile = new fs::path();
m_destinationFile = new fs::path();
}
Scene::Scene(Scene *scene) : NetworkedObject(Properties()) {
m_kdtree = scene->m_kdtree;
m_blockSize = scene->m_blockSize;
m_aabb = scene->m_aabb;
m_environmentEmitter = scene->m_environmentEmitter;
m_sensor = scene->m_sensor;
m_integrator = scene->m_integrator;
m_sourceFile = new fs::path(*scene->m_sourceFile);
m_destinationFile = new fs::path(*scene->m_destinationFile);
m_emitterPDF = scene->m_emitterPDF;
m_shapes = scene->m_shapes;
m_sensors = scene->m_sensors;
m_meshes = scene->m_meshes;
m_emitters = scene->m_emitters;
m_media = scene->m_media;
m_ssIntegrators = scene->m_ssIntegrators;
m_objects = scene->m_objects;
m_netObjects = scene->m_netObjects;
m_specialShapes = scene->m_specialShapes;
m_degenerateSensor = scene->m_degenerateSensor;
m_degenerateEmitters = scene->m_degenerateEmitters;
}
Scene::Scene(Stream *stream, InstanceManager *manager)
: NetworkedObject(stream, manager) {
m_kdtree = new ShapeKDTree();
m_kdtree->setQueryCost(stream->readFloat());
m_kdtree->setTraversalCost(stream->readFloat());
m_kdtree->setEmptySpaceBonus(stream->readFloat());
m_kdtree->setStopPrims(stream->readInt());
m_kdtree->setClip(stream->readBool());
m_kdtree->setMaxDepth(stream->readUInt());
m_kdtree->setExactPrimitiveThreshold(stream->readUInt());
m_kdtree->setParallelBuild(stream->readBool());
m_kdtree->setRetract(stream->readBool());
m_kdtree->setMaxBadRefines(stream->readUInt());
m_blockSize = stream->readUInt();
m_degenerateSensor = stream->readBool();
m_degenerateEmitters = stream->readBool();
m_aabb = AABB(stream);
m_environmentEmitter = static_cast<Emitter *>(manager->getInstance(stream));
m_sourceFile = new fs::path(stream->readString());
m_destinationFile = new fs::path(stream->readString());
size_t count = stream->readSize();
m_shapes.reserve(count);
for (size_t i=0; i<count; ++i)
m_shapes.push_back(static_cast<Shape *>(manager->getInstance(stream)));
count = stream->readSize();
m_specialShapes.reserve(count);
for (size_t i=0; i<count; ++i)
m_specialShapes.push_back(static_cast<Shape *>(manager->getInstance(stream)));
count = stream->readSize();
m_meshes.reserve(count);
for (size_t i=0; i<count; ++i)
m_meshes.push_back(static_cast<TriMesh *>(manager->getInstance(stream)));
count = stream->readSize();
m_sensors.reserve(count);
for (size_t i=0; i<count; ++i)
m_sensors.push_back(static_cast<Sensor *>(manager->getInstance(stream)));
count = stream->readSize();
m_emitters.reserve(count);
for (size_t i=0; i<count; ++i)
m_emitters.push_back(static_cast<Emitter *>(manager->getInstance(stream)));
count = stream->readSize();
m_media.reserve(count);
for (size_t i=0; i<count; ++i)
m_media.push_back(static_cast<Medium *>(manager->getInstance(stream)));
count = stream->readSize();
m_ssIntegrators.reserve(count);
for (size_t i=0; i<count; ++i)
m_ssIntegrators.push_back(static_cast<Subsurface *>(manager->getInstance(stream)));
count = stream->readSize();
m_objects.reserve(count);
for (size_t i=0; i<count; ++i)
m_objects.push_back(static_cast<ConfigurableObject *>(manager->getInstance(stream)));
count = stream->readSize();
m_netObjects.reserve(count);
for (size_t i=0; i<count; ++i)
m_netObjects.push_back(static_cast<NetworkedObject *>(manager->getInstance(stream)));
initialize();
}
Scene::~Scene() {
delete m_destinationFile;
delete m_sourceFile;
}
void Scene::serialize(Stream *stream, InstanceManager *manager) const {
ConfigurableObject::serialize(stream, manager);
stream->writeFloat(m_kdtree->getQueryCost());
stream->writeFloat(m_kdtree->getTraversalCost());
stream->writeFloat(m_kdtree->getEmptySpaceBonus());
stream->writeInt(m_kdtree->getStopPrims());
stream->writeBool(m_kdtree->getClip());
stream->writeUInt(m_kdtree->getMaxDepth());
stream->writeUInt(m_kdtree->getExactPrimitiveThreshold());
stream->writeBool(m_kdtree->getParallelBuild());
stream->writeBool(m_kdtree->getRetract());
stream->writeUInt(m_kdtree->getMaxBadRefines());
stream->writeUInt(m_blockSize);
stream->writeBool(m_degenerateSensor);
stream->writeBool(m_degenerateEmitters);
m_aabb.serialize(stream);
manager->serialize(stream, m_environmentEmitter.get());
stream->writeString(m_sourceFile->string());
stream->writeString(m_destinationFile->string());
stream->writeSize(m_shapes.size());
for (size_t i=0; i<m_shapes.size(); ++i)
manager->serialize(stream, m_shapes[i].get());
stream->writeSize(m_specialShapes.size());
for (size_t i=0; i<m_specialShapes.size(); ++i)
manager->serialize(stream, m_specialShapes[i].get());
stream->writeSize(m_meshes.size());
for (size_t i=0; i<m_meshes.size(); ++i)
manager->serialize(stream, m_meshes[i]);
stream->writeSize(m_sensors.size());
for (size_t i=0; i<m_sensors.size(); ++i)
manager->serialize(stream, m_sensors[i].get());
stream->writeSize(m_emitters.size());
for (size_t i=0; i<m_emitters.size(); ++i)
manager->serialize(stream, m_emitters[i].get());
stream->writeSize(m_media.size());
for (ref_vector<Medium>::const_iterator it = m_media.begin();
it != m_media.end(); ++it)
manager->serialize(stream, it->get());
stream->writeSize(m_ssIntegrators.size());
for (ref_vector<Subsurface>::const_iterator it = m_ssIntegrators.begin();
it != m_ssIntegrators.end(); ++it)
manager->serialize(stream, it->get());
stream->writeSize(m_objects.size());
for (ref_vector<ConfigurableObject>::const_iterator it = m_objects.begin();
it != m_objects.end(); ++it)
manager->serialize(stream, it->get());
stream->writeSize(m_netObjects.size());
for (ref_vector<NetworkedObject>::const_iterator it = m_netObjects.begin();
it != m_netObjects.end(); ++it)
manager->serialize(stream, it->get());
}
// ===========================================================================
// Scene initialization, rendering, and miscellaneous methods
// ===========================================================================
void Scene::bindUsedResources(ParallelProcess *proc) const {
for (ref_vector<NetworkedObject>::const_iterator it = m_netObjects.begin();
it != m_netObjects.end(); ++it)
it->get()->bindUsedResources(proc);
}
void Scene::wakeup(ConfigurableObject *,
std::map<std::string, SerializableObject *> &params) {
for (ref_vector<NetworkedObject>::iterator it = m_netObjects.begin();
it != m_netObjects.end(); ++it)
(*it)->wakeup(this, params);
}
void Scene::setSensor(Sensor *sensor) {
m_sensor = sensor;
m_degenerateSensor = sensor->getType() & Sensor::EDeltaPosition;
}
void Scene::removeSensor(Sensor *sensor) {
if (!sensor)
return;
ref<Sensor> oldSensor = sensor;
m_sensors.erase(std::remove(m_sensors.begin(),
m_sensors.end(), oldSensor));
}
void Scene::addSensor(Sensor *sensor) {
ref<Sensor> newSensor = sensor;
if (!newSensor || std::find(m_sensors.begin(),
m_sensors.end(), newSensor) != m_sensors.end())
return;
m_sensors.push_back(newSensor);
}
void Scene::configure() {
if (m_integrator == NULL) {
/* Create a direct integrator by default */
m_integrator = static_cast<Integrator *> (PluginManager::getInstance()->
createObject(MTS_CLASS(Integrator), Properties("direct")));
m_integrator->configure();
}
if (m_sensor == NULL) {
if (m_sensors.size() == 0) {
Log(EInfo, "No sensors found! Adding a perspective camera..");
Properties props("perspective");
props.setFloat("fov", 45.0f);
/* Create a perspective camera with a 45 deg. field of view
and positioned so that it can see the entire scene */
AABB aabb;
for (ref_vector<Shape>::iterator it = m_shapes.begin();
it != m_shapes.end(); ++it)
aabb.expandBy(it->get()->getAABB());
if (aabb.isValid()) {
Point center = aabb.getCenter();
Vector extents = aabb.getExtents();
Float maxExtentsXY = std::max(extents.x, extents.y);
Float distance = maxExtentsXY/(2.0f * std::tan(45 * .5f * M_PI/180));
Float maxExtentsXYZ = std::max(extents.z, maxExtentsXY);
props.setFloat("farClip", maxExtentsXYZ * 5 + distance);
props.setFloat("nearClip", distance / 100);
props.setFloat("focusDistance", distance + extents.z/2);
props.setTransform("toWorld", Transform::translate(Vector(center.x,
center.y, aabb.min.z - distance)));
}
Sensor *sensor = static_cast<Sensor *> (PluginManager::getInstance()->
createObject(MTS_CLASS(Sensor), props));
sensor->configure();
m_sensors.push_back(sensor);
}
m_sensor = m_sensors[m_sensors.size()-1];
}
m_sampler = m_sensor->getSampler();
m_integrator->configureSampler(this, m_sampler);
}
void Scene::invalidate() {
m_kdtree = new ShapeKDTree();
}
void Scene::initialize() {
if (!m_kdtree->isBuilt()) {
/* Expand all geometry */
ref_vector<Shape> temp;
temp.reserve(m_shapes.size());
m_shapes.ensureUnique();
m_shapes.swap(temp);
size_t primitiveCount = 0, effPrimitiveCount = 0;
for (size_t i=0; i<temp.size(); ++i) {
addShape(temp[i]);
primitiveCount += temp[i]->getPrimitiveCount();
effPrimitiveCount += temp[i]->getEffectivePrimitiveCount();
temp[i] = NULL;
}
if (primitiveCount != effPrimitiveCount) {
Log(EDebug, "Scene contains " SIZE_T_FMT " primitives. Due to "
"instancing or other kinds of procedural geometry, the effective number of primitives is "
SIZE_T_FMT ".", primitiveCount, effPrimitiveCount);
}
/* Build the kd-tree */
m_kdtree->build();
m_aabb = m_kdtree->getAABB();
}
/* Make sure that there are no duplicates */
m_emitters.ensureUnique();
m_media.ensureUnique();
m_ssIntegrators.ensureUnique();
m_objects.ensureUnique();
m_netObjects.ensureUnique();
if (!m_emitterPDF.isNormalized()) {
if (m_emitters.size() == 0) {
Log(EWarn, "No emitters found -- adding sun & sky.");
/* This is not a particularly realistic sky -- it extends below the
horizon and uses an enlarged sun :). This is done to get better
results for arbitrary input (and with a path tracer). */
Properties skyProps("sunsky");
skyProps.setFloat("scale", 2);
skyProps.setTransform("toWorld", Transform::rotate(Vector(0,1,0), -120.0f));
skyProps.setBoolean("extend", true);
skyProps.setFloat("sunRadiusScale", 15);
ref<Emitter> emitter = static_cast<Emitter *>(
PluginManager::getInstance()->createObject(MTS_CLASS(Emitter), skyProps));
addChild(emitter);
emitter->configure();
}
/* Calculate a discrete PDF to importance sample emitters */
for (ref_vector<Emitter>::iterator it = m_emitters.begin();
it != m_emitters.end(); ++it)
m_emitterPDF.append(it->get()->getSamplingWeight());
m_emitterPDF.normalize();
}
initializeBidirectional();
}
void Scene::initializeBidirectional() {
m_aabb = m_kdtree->getAABB();
m_degenerateEmitters = true;
m_specialShapes.clear();
if (m_sensor) {
ref<Shape> shape = m_sensor->createShape(this);
if (shape != NULL)
m_specialShapes.push_back(shape);
m_aabb.expandBy(m_sensor->getAABB());
m_degenerateSensor = m_sensor->getType() & Sensor::EDeltaPosition;
}
AABB aabb(m_aabb);
for (ref_vector<Emitter>::iterator it = m_emitters.begin();
it != m_emitters.end(); ++it) {
Emitter *emitter = it->get();
ref<Shape> shape = emitter->createShape(this);
if (shape != NULL)
m_specialShapes.push_back(shape);
aabb.expandBy(emitter->getAABB());
if (!(emitter->getType() & Emitter::EDeltaPosition))
m_degenerateEmitters = false;
}
m_aabb = aabb;
}
bool Scene::preprocess(RenderQueue *queue, const RenderJob *job,
int sceneResID, int sensorResID, int samplerResID) {
initialize();
/* Pre-process step for the main scene integrator */
if (!m_integrator->preprocess(this, queue, job,
sceneResID, sensorResID, samplerResID))
return false;
/* Pre-process step for all sub-surface integrators (each one in independence) */
for (ref_vector<Subsurface>::iterator it = m_ssIntegrators.begin();
it != m_ssIntegrators.end(); ++it)
(*it)->setActive(false);
for (ref_vector<Subsurface>::iterator it = m_ssIntegrators.begin();
it != m_ssIntegrators.end(); ++it)
if (!(*it)->preprocess(this, queue, job,
sceneResID, sensorResID, samplerResID))
return false;
for (ref_vector<Subsurface>::iterator it = m_ssIntegrators.begin();
it != m_ssIntegrators.end(); ++it)
(*it)->setActive(true);
return true;
}
bool Scene::render(RenderQueue *queue, const RenderJob *job,
int sceneResID, int sensorResID, int samplerResID) {
m_sensor->getFilm()->clear();
return m_integrator->render(this, queue, job, sceneResID,
sensorResID, samplerResID);
}
void Scene::cancel() {
for (ref_vector<Subsurface>::iterator it = m_ssIntegrators.begin();
it != m_ssIntegrators.end(); ++it)
(*it)->cancel();
m_integrator->cancel();
}
void Scene::flush(RenderQueue *queue, const RenderJob *job) {
m_sensor->getFilm()->develop(this, queue->getRenderTime(job));
}
void Scene::setDestinationFile(const fs::path &name) {
*m_destinationFile = name;
}
void Scene::setSourceFile(const fs::path &name) {
*m_sourceFile = name;
}
void Scene::postprocess(RenderQueue *queue, const RenderJob *job,
int sceneResID, int sensorResID, int samplerResID) {
m_integrator->postprocess(this, queue, job, sceneResID,
sensorResID, samplerResID);
m_sensor->getFilm()->develop(this, queue->getRenderTime(job));
}
void Scene::addChild(const std::string &name, ConfigurableObject *child) {
const Class *cClass = child->getClass();
if (cClass->derivesFrom(MTS_CLASS(NetworkedObject)) &&
!cClass->derivesFrom(MTS_CLASS(Integrator)))
m_netObjects.push_back(static_cast<NetworkedObject *>(child));
if (cClass->derivesFrom(MTS_CLASS(Sensor))) {
m_sensors.push_back(static_cast<Sensor *>(child));
} else if (cClass->derivesFrom(MTS_CLASS(Integrator))) {
AssertEx(m_integrator == NULL, "There can only be one integrator per scene");
m_integrator = static_cast<Integrator *>(child);
} else if (cClass->derivesFrom(MTS_CLASS(Texture))
|| cClass->derivesFrom(MTS_CLASS(BSDF))
|| cClass->derivesFrom(MTS_CLASS(Subsurface))
|| cClass->derivesFrom(MTS_CLASS(PhaseFunction))) {
m_objects.push_back(static_cast<ConfigurableObject *>(child));
} else if (cClass->derivesFrom(MTS_CLASS(Medium))) {
m_media.push_back(static_cast<Medium *>(child));
} else if (cClass->derivesFrom(MTS_CLASS(Emitter))) {
Emitter *emitter = static_cast<Emitter *>(child);
if (emitter->isCompound()) {
size_t index = 0;
do {
ref<Emitter> element = emitter->getElement(index++);
if (element == NULL)
break;
addChild(name, element);
} while (true);
return;
}
if (emitter->isEnvironmentEmitter()) {
if (m_environmentEmitter != NULL)
Log(EError, "The scene may only contain one environment emitter");
m_environmentEmitter = emitter;
}
m_emitters.push_back(emitter);
} else if (cClass->derivesFrom(MTS_CLASS(Shape))) {
Shape *shape = static_cast<Shape *>(child);
if (shape->isSensor()) // determine sensors as early as possible
addSensor(shape->getSensor());
m_shapes.push_back(shape);
} else if (cClass->derivesFrom(MTS_CLASS(Scene))) {
ref<Scene> scene = static_cast<Scene *>(child);
/* A scene from somewhere else has been included.
Add all of its contents */
ref_vector<Emitter> &emitters = scene->getEmitters();
ref_vector<Sensor> &sensors = scene->getSensors();
ref_vector<Shape> &shapes = scene->getShapes();
ref_vector<ConfigurableObject> &refObjects = scene->getReferencedObjects();
for (size_t i=0; i<emitters.size(); ++i)
addChild(emitters[i]);
for (size_t i=0; i<sensors.size(); ++i)
addChild(sensors[i]);
for (size_t i=0; i<shapes.size(); ++i)
addChild(shapes[i]);
for (ref_vector<ConfigurableObject>::iterator it = refObjects.begin();
it != refObjects.end(); ++it)
addChild(it->get());
for (ref_vector<Medium>::iterator it = scene->getMedia().begin();
it != scene->getMedia().end(); ++it)
addChild(it->get());
if (scene->getIntegrator() != NULL)
addChild(scene->getIntegrator());
if (scene->getSensor() != NULL)
addChild(scene->getSensor());
} else {
ConfigurableObject::addChild(name, child);
}
}
void Scene::addShape(Shape *shape) {
if (shape->isCompound()) {
int index = 0;
do {
ref<Shape> element = shape->getElement(index++);
if (element == NULL)
break;
addShape(element);
} while (true);
} else {
if (shape->isSensor() && !m_sensors.contains(shape->getSensor()))
m_sensors.push_back(shape->getSensor());
if (shape->isEmitter())
m_emitters.push_back(shape->getEmitter());
if (shape->hasSubsurface()) {
m_netObjects.push_back(shape->getSubsurface());
m_ssIntegrators.push_back(shape->getSubsurface());
}
Medium *iMedium = shape->getInteriorMedium(),
*eMedium = shape->getExteriorMedium();
if (eMedium != NULL)
m_media.push_back(eMedium);
if (iMedium != NULL)
m_media.push_back(iMedium);
if (shape->getClass()->derivesFrom(MTS_CLASS(TriMesh)))
m_meshes.push_back(static_cast<TriMesh *>(shape));
m_kdtree->addShape(shape);
m_shapes.push_back(shape);
}
}
std::string Scene::toString() const {
std::ostringstream oss;
oss << "Scene[" << endl
<< " sensor = " << indent(m_sensor.toString()) << "," << endl
<< " sampler = " << indent(m_sampler.toString()) << "," << endl
<< " integrator = " << indent(m_integrator.toString()) << "," << endl
<< " kdtree = " << indent(m_kdtree.toString()) << "," << endl
<< " environmentEmitter = " << indent(m_environmentEmitter.toString()) << "," << endl
<< " shapes = " << indent(containerToString(m_shapes.begin(), m_shapes.end())) << "," << endl
<< " emitters = " << indent(containerToString(m_emitters.begin(), m_emitters.end())) << "," << endl
<< " media = " << indent(containerToString(m_media.begin(), m_media.end())) << "," << endl
<< " sensors = " << indent(containerToString(m_sensors.begin(), m_sensors.end())) << "," << endl
<< " ssIntegrators = " << indent(containerToString(m_ssIntegrators.begin(), m_ssIntegrators.end())) << "," << endl
<< " objects = " << indent(containerToString(m_objects.begin(), m_objects.end())) << endl
<< "]";
return oss.str();
}
// ===========================================================================
// Ray tracing and transmittance computations
// ===========================================================================
static StatsCounter mediumInconsistencies("General", "Detected medium inconsistencies");
Spectrum Scene::evalTransmittance(const Point &p1, bool p1OnSurface, const Point &p2, bool p2OnSurface,
Float time, const Medium *medium, int &interactions, Sampler *sampler) const {
Vector d = p2 - p1;
Float remaining = d.length();
d /= remaining;
Float lengthFactor = p2OnSurface ? (1-ShadowEpsilon) : 1;
Ray ray(p1, d, p1OnSurface ? Epsilon : 0, remaining * lengthFactor, time);
Spectrum transmittance(1.0f);
Intersection its;
int maxInteractions = interactions;
interactions = 0;
while (remaining > 0) {
Normal n;
bool surface = rayIntersect(ray, its.t, its.shape, its.geoFrame.n, its.uv);
if (surface && (interactions == maxInteractions ||
!(its.getBSDF()->getType() & BSDF::ENull))) {
/* Encountered an occluder -- zero transmittance. */
return Spectrum(0.0f);
}
if (medium)
transmittance *= medium->evalTransmittance(
Ray(ray, 0, std::min(its.t, remaining)), sampler);
if (!surface || transmittance.isZero())
break;
const BSDF *bsdf = its.getBSDF();
its.p = ray.o;
its.geoFrame = Frame(its.geoFrame.n);
its.hasUVPartials = false;
Vector wo = its.geoFrame.toLocal(ray.d);
BSDFSamplingRecord bRec(its, -wo, wo, ERadiance);
bRec.typeMask = BSDF::ENull;
transmittance *= bsdf->eval(bRec, EDiscrete);
if (its.isMediumTransition()) {
if (medium != its.getTargetMedium(-d)) {
++mediumInconsistencies;
return Spectrum(0.0f);
}
medium = its.getTargetMedium(d);
}
if (++interactions > 100) { /// Just a precaution..
Log(EWarn, "evalTransmittance(): round-off error issues?");
break;
}
ray.o = ray(its.t);
remaining -= its.t;
ray.maxt = remaining * lengthFactor;
ray.mint = Epsilon;
}
return transmittance;
}
// ===========================================================================
// Ray tracing support for bidirectional algorithms
// ===========================================================================
bool Scene::rayIntersectAll(const Ray &ray) const {
if (rayIntersect(ray))
return true;
Float mint = ray.mint;
if (mint == Epsilon)
mint *= std::max(std::max(std::max(std::abs(ray.o.x),
std::abs(ray.o.y)), std::abs(ray.o.z)), Epsilon);
for (size_t i=0; i<m_specialShapes.size(); ++i) {
if (m_specialShapes[i]->rayIntersect(ray, mint, ray.maxt))
return true;
}
return false;
}
bool Scene::rayIntersectAll(const Ray &ray, Float &t,
ConstShapePtr &shapePtr, Normal &n, Point2 &uv) const {
bool result = rayIntersect(ray, t, shapePtr, n, uv);
if (m_specialShapes.size() == 0)
return result;
uint8_t buffer[MTS_KD_INTERSECTION_TEMP];
Float tempT, maxt = result ? t : ray.maxt;
Float mint = ray.mint;
if (mint == Epsilon)
mint *= std::max(std::max(std::max(std::abs(ray.o.x),
std::abs(ray.o.y)), std::abs(ray.o.z)), Epsilon);
for (size_t i=0; i<m_specialShapes.size(); ++i) {
const Shape *shape = m_specialShapes[i].get();
if (shape->rayIntersect(ray, mint, maxt, tempT, buffer)) {
/// Uh oh... -- much unnecessary work is done here
Intersection its;
its.t = tempT;
shape->fillIntersectionRecord(ray, buffer, its);
maxt = t = tempT;
shapePtr = shape;
n = its.geoFrame.n;
uv = its.uv;
result = true;
}
}
return result;
}
bool Scene::rayIntersectAll(const Ray &ray, Intersection &its) const {
bool result = rayIntersect(ray, its);
if (m_specialShapes.size() == 0)
return result;
uint8_t buffer[MTS_KD_INTERSECTION_TEMP];
Float maxt = result ? its.t : ray.maxt;
Float mint = ray.mint;
if (mint == Epsilon)
mint *= std::max(std::max(std::max(std::abs(ray.o.x),
std::abs(ray.o.y)), std::abs(ray.o.z)), Epsilon);
Float tempT;
for (size_t i=0; i<m_specialShapes.size(); ++i) {
const Shape *shape = m_specialShapes[i].get();
if (shape->rayIntersect(ray, mint, maxt, tempT, buffer)) {
its.t = tempT;
shape->fillIntersectionRecord(ray, buffer, its);
result = true;
}
}
return result;
}
Spectrum Scene::evalTransmittanceAll(const Point &p1, bool p1OnSurface, const Point &p2, bool p2OnSurface,
Float time, const Medium *medium, int &interactions, Sampler *sampler) const {
Vector d = p2 - p1;
Float remaining = d.length();
d /= remaining;
Float lengthFactor = p2OnSurface ? (1-ShadowEpsilon) : 1;
Ray ray(p1, d, p1OnSurface ? Epsilon : 0, remaining * lengthFactor, time);
Spectrum transmittance(1.0f);
Intersection its;
int maxInteractions = interactions;
interactions = 0;
while (remaining > 0) {
Normal n;
bool surface = rayIntersectAll(ray, its.t, its.shape, its.geoFrame.n, its.uv);
if (surface && (interactions == maxInteractions ||
!(its.getBSDF()->getType() & BSDF::ENull))) {
/* Encountered an occluder -- zero transmittance. */
return Spectrum(0.0f);
}
if (medium)
transmittance *= medium->evalTransmittance(
Ray(ray, 0, std::min(its.t, remaining)), sampler);
if (!surface || transmittance.isZero())
break;
const BSDF *bsdf = its.getBSDF();
its.p = ray.o;
its.geoFrame = Frame(its.geoFrame.n);
its.hasUVPartials = false;
Vector wo = its.geoFrame.toLocal(ray.d);
BSDFSamplingRecord bRec(its, -wo, wo, ERadiance);
bRec.typeMask = BSDF::ENull;
transmittance *= bsdf->eval(bRec, EDiscrete);
if (its.isMediumTransition()) {
if (medium != its.getTargetMedium(-d)) {
++mediumInconsistencies;
return Spectrum(0.0f);
}
medium = its.getTargetMedium(d);
}
if (++interactions > 100) { /// Just a precaution..
Log(EWarn, "evalTransmittanceAll(): round-off error issues?");
break;
}
ray.o = ray(its.t);
remaining -= its.t;
ray.maxt = remaining * lengthFactor;
ray.mint = Epsilon;
}
return transmittance;
}
// ===========================================================================
// Emission and direct illumination sampling
// ===========================================================================
Spectrum Scene::sampleEmitterDirect(DirectSamplingRecord &dRec,
const Point2 &_sample, bool testVisibility) const {
Point2 sample(_sample);
/* Randomly pick an emitter */
Float emPdf;
size_t index = m_emitterPDF.sampleReuse(sample.x, emPdf);
const Emitter *emitter = m_emitters[index].get();
Spectrum value = emitter->sampleDirect(dRec, sample);
if (dRec.pdf != 0) {
if (testVisibility) {
Ray ray(dRec.ref, dRec.d, Epsilon,
dRec.dist*(1-ShadowEpsilon), dRec.time);
if (m_kdtree->rayIntersect(ray))
return Spectrum(0.0f);
}
dRec.object = emitter;
dRec.pdf *= emPdf;
value /= emPdf;
return value;
} else {
return Spectrum(0.0f);
}
}
Spectrum Scene::sampleAttenuatedEmitterDirect(DirectSamplingRecord &dRec,
const Medium *medium, int &interactions, const Point2 &_sample, Sampler *sampler) const {
Point2 sample(_sample);
/* Randomly pick an emitter */
Float emPdf;
size_t index = m_emitterPDF.sampleReuse(sample.x, emPdf);
const Emitter *emitter = m_emitters[index].get();
Spectrum value = emitter->sampleDirect(dRec, sample);
if (dRec.pdf != 0) {
value *= evalTransmittance(dRec.ref, false,
dRec.p, emitter->isOnSurface(), dRec.time, medium,
interactions, sampler) / emPdf;
dRec.object = emitter;
dRec.pdf *= emPdf;
return value;
} else {
return Spectrum(0.0f);
}
}
Spectrum Scene::sampleAttenuatedEmitterDirect(DirectSamplingRecord &dRec,
const Intersection &its, const Medium *medium, int &interactions,
const Point2 &_sample, Sampler *sampler) const {
Point2 sample(_sample);
/* Randomly pick an emitter */
Float emPdf;
size_t index = m_emitterPDF.sampleReuse(sample.x, emPdf);
const Emitter *emitter = m_emitters[index].get();
Spectrum value = emitter->sampleDirect(dRec, sample);
if (dRec.pdf != 0) {
if (its.shape && its.isMediumTransition())
medium = its.getTargetMedium(dRec.d);
value *= evalTransmittance(its.p, true, dRec.p, emitter->isOnSurface(),
dRec.time, medium, interactions, sampler) / emPdf;
dRec.object = emitter;
dRec.pdf *= emPdf;
return value;
} else {
return Spectrum(0.0f);
}
}
Spectrum Scene::sampleSensorDirect(DirectSamplingRecord &dRec,
const Point2 &sample, bool testVisibility) const {
Spectrum value = m_sensor->sampleDirect(dRec, sample);
if (dRec.pdf != 0) {
if (testVisibility) {
Ray ray(dRec.ref, dRec.d, Epsilon,
dRec.dist*(1-ShadowEpsilon), dRec.time);
if (m_kdtree->rayIntersect(ray))
return Spectrum(0.0f);
}
dRec.object = m_sensor.get();
return value;
} else {
return Spectrum(0.0f);
}
}
Spectrum Scene::sampleAttenuatedSensorDirect(DirectSamplingRecord &dRec,
const Medium *medium, int &interactions, const Point2 &sample, Sampler *sampler) const {
Spectrum value = m_sensor->sampleDirect(dRec, sample);
if (dRec.pdf != 0) {
value *= evalTransmittance(dRec.ref, false, dRec.p, m_sensor->isOnSurface(),
dRec.time, medium, interactions, sampler);
dRec.object = m_sensor.get();
return value;
} else {
return Spectrum(0.0f);
}
}
Spectrum Scene::sampleAttenuatedSensorDirect(DirectSamplingRecord &dRec,
const Intersection &its, const Medium *medium, int &interactions,
const Point2 &sample, Sampler *sampler) const {
Spectrum value = m_sensor->sampleDirect(dRec, sample);
if (dRec.pdf != 0) {
if (its.shape && its.isMediumTransition())
medium = its.getTargetMedium(dRec.d);
value *= evalTransmittance(its.p, true, dRec.p, m_sensor->isOnSurface(),
dRec.time, medium, interactions, sampler);
dRec.object = m_sensor.get();
return value;
} else {
return Spectrum(0.0f);
}
}
Float Scene::pdfEmitterDirect(const DirectSamplingRecord &dRec) const {
const Emitter *emitter = static_cast<const Emitter *>(dRec.object);
return emitter->pdfDirect(dRec) * pdfEmitterDiscrete(emitter);
}
Float Scene::pdfSensorDirect(const DirectSamplingRecord &dRec) const {
return m_sensor->pdfDirect(dRec);
}
Spectrum Scene::sampleEmitterPosition(
PositionSamplingRecord &pRec,
const Point2 &_sample) const {
Point2 sample(_sample);
/* Randomly pick an emitter */
Float emPdf;
size_t index = m_emitterPDF.sampleReuse(sample.x, emPdf);
const Emitter *emitter = m_emitters[index].get();
Spectrum value = emitter->samplePosition(pRec, sample);
pRec.object = emitter;
pRec.pdf *= emPdf;
return value / emPdf;
}
Float Scene::pdfEmitterPosition(const PositionSamplingRecord &pRec) const {
const Emitter *emitter = static_cast<const Emitter *>(pRec.object);
return emitter->pdfPosition(pRec) * pdfEmitterDiscrete(emitter);
}
Spectrum Scene::sampleEmitterRay(Ray &ray,
const Emitter* &emitter,
const Point2 &spatialSample,
const Point2 &directionalSample,
Float time) const {
Point2 sample(spatialSample);
/* Randomly pick an emitter */
Float emPdf;
size_t index = m_emitterPDF.sampleReuse(sample.x, emPdf);
emitter = m_emitters[index].get();
return emitter->sampleRay(ray, sample, directionalSample, time) / emPdf;
}
MTS_IMPLEMENT_CLASS_S(Scene, false, ConfigurableObject)
MTS_NAMESPACE_END