/* 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> MTS_NAMESPACE_BEGIN /*! \plugin{ao}{Ambient occlusion integrator} * \order{0} * \parameters{ * \parameter{shadingSamples}{\Integer}{Specifies the number of * shading samples that should be computed per primary ray * \default{1}} * * \parameter{rayLength}{\Float}{Specifies the world-space length of the * ambient occlusion rays that will be cast. \default{\code{-1}, i.e. automatic}}. * } * \renderings{ * \rendering{A view of the scene on page \pageref{fig:rungholt}, rendered using * the Ambient Occlusion integrator}{integrator_ao} * \rendering{A corresponding rendering created using the standard \pluginref{path} tracer}{integrator_ao_path} * } * Ambient Occlusion is a simple non-photorealistic rendering technique that simulates the exposure of an object * to uniform illumination incident from all direction. It produces approximate shadowing between closeby * objects, as well as darkening in corners, creases, and cracks. The scattering models associated with objects * in the scene are ignored. */ class AmbientOcclusionIntegrator : public SamplingIntegrator { public: AmbientOcclusionIntegrator(const Properties &props) : SamplingIntegrator(props) { m_shadingSamples = props.getSize("shadingSamples", 1); m_rayLength = props.getFloat("rayLength", -1); } /// Unserialize from a binary data stream AmbientOcclusionIntegrator(Stream *stream, InstanceManager *manager) : SamplingIntegrator(stream, manager) { m_shadingSamples = stream->readSize(); m_rayLength = stream->readFloat(); configure(); } void serialize(Stream *stream, InstanceManager *manager) const { SamplingIntegrator::serialize(stream, manager); stream->writeSize(m_shadingSamples); stream->writeFloat(m_rayLength); } void configureSampler(const Scene *scene, Sampler *sampler) { SamplingIntegrator::configureSampler(scene, sampler); if (m_shadingSamples > 1) sampler->request2DArray(m_shadingSamples); } bool preprocess(const Scene *scene, RenderQueue *queue, const RenderJob *job, int sceneResID, int sensorResID, int samplerResID) { Integrator::preprocess(scene, queue, job, sceneResID, sensorResID, samplerResID); if (m_rayLength < 0) { m_rayLength = scene->getAABB().getBSphere().radius * 0.5f; Log(EInfo, "Setting occlusion ray length to %f", m_rayLength); } return true; } Spectrum Li(const RayDifferential &ray, RadianceQueryRecord &rRec) const { /* Some aliases and local variables */ Spectrum Li(0.0f); Point2 sample; /* Perform the first ray intersection (or ignore if the intersection has already been provided). */ if (!rRec.rayIntersect(ray)) { /* If no intersection could be found, possibly return radiance from a background emitter */ return Spectrum(1.0f); } /* Figure out how many shading samples to take, and where the required random numbers should come from */ Point2 *sampleArray = &sample; size_t numShadingSamples = m_shadingSamples; bool adaptiveQuery = (rRec.extra & RadianceQueryRecord::EAdaptiveQuery); if (numShadingSamples > 1 && rRec.depth == 1 && !adaptiveQuery) { sampleArray = rRec.sampler->next2DArray(numShadingSamples); } else { /* This integrator is used recursively by another integrator. Be less accurate as this sample will not directly be observed. */ numShadingSamples = 1; sample = rRec.nextSample2D(); } const Intersection &its = rRec.its; for (size_t i=0; i<numShadingSamples; ++i) { Vector d = its.toWorld(warp::squareToCosineHemisphere(sampleArray[i])); Ray shadowRay(its.p, d, Epsilon, m_rayLength, ray.time); if (!rRec.scene->rayIntersect(shadowRay)) Li += Spectrum(1.0f); } Li /= static_cast<Float>(numShadingSamples); return Li; } std::string toString() const { std::ostringstream oss; oss << "AmbientOcclusionIntegrator[" << endl << " shadingSamples = " << m_shadingSamples << "," << endl << " rayLength = " << m_rayLength << endl << "]"; return oss.str(); } MTS_DECLARE_CLASS() private: size_t m_shadingSamples; Float m_rayLength; }; MTS_IMPLEMENT_CLASS_S(AmbientOcclusionIntegrator, false, SamplingIntegrator) MTS_EXPORT_PLUGIN(AmbientOcclusionIntegrator, "Ambient occlusion integrator"); MTS_NAMESPACE_END