/* This file is part of Mitsuba, a physically based rendering system. Copyright (c) 2007-2010 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 . */ #include #include #include "irrtree.h" MTS_NAMESPACE_BEGIN /** * Computes the combined diffuse radiant exitance * caused by a number of dipole sources */ struct IsotropicDipoleQuery { #if !defined(MTS_SSE) || (SPECTRUM_SAMPLES != 3) || 1 inline IsotropicDipoleQuery(const Spectrum &zr, const Spectrum &zv, const Spectrum &sigmaTr, Float Fdt, const Point &p) : zr(zr), zv(zv), sigmaTr(sigmaTr), Fdt(Fdt), p(p) { count = 0; } inline void operator()(const IrradianceSample &sample) { Spectrum rSqr = Spectrum((p - sample.p).lengthSquared()); /* Distance to the real source */ Spectrum dr = (rSqr + zr*zr).sqrt(); /* Distance to the image point source */ Spectrum dv = (rSqr + zv*zv).sqrt(); Spectrum C1 = zr * (sigmaTr + Spectrum(1.0f) / dr); Spectrum C2 = zv * (sigmaTr + Spectrum(1.0f) / dv); /* Do not include the reduced albedo - will be canceled out later */ Spectrum dMo = Spectrum(0.25f * INV_PI) * (C1 * ((-sigmaTr * dr).exp()) / (dr * dr) + C2 * ((-sigmaTr * dv).exp()) / (dv * dv)); result += dMo * sample.E * (sample.area * Fdt); count++; } inline const Spectrum &getResult() const { return result; } Spectrum zr, zv, sigmaTr, result; #else inline IsotropicDipoleQuery(const Spectrum &_zr, const Spectrum &_zv, const Spectrum &_sigmaTr, Float Fdt, const Point &p) : Fdt(Fdt), p(p) { zr = _mm_set_ps(_zr[0], _zr[1], _zr[2], 0); zv = _mm_set_ps(_zv[0], _zv[1], _zv[2], 0); sigmaTr = _mm_set_ps(_sigmaTr[0], _sigmaTr[1], _sigmaTr[2], 0); zrSqr = _mm_mul_ps(zr, zr); zvSqr = _mm_mul_ps(zv, zv); result.ps = _mm_setzero_ps(); count = 0; } inline void operator()(const IrradianceSample &sample) { /* Distance to the positive point source of the dipole */ const __m128 lengthSquared = _mm_set1_ps((p - sample.p).lengthSquared()), drSqr = _mm_add_ps(zrSqr, lengthSquared), dvSqr = _mm_add_ps(zvSqr, lengthSquared), dr = _mm_sqrt_ps(drSqr), dv = _mm_sqrt_ps(dvSqr), one = _mm_set1_ps(1.0f), factor = _mm_mul_ps(_mm_set1_ps(0.25f*INV_PI*sample.area * Fdt), _mm_set_ps(sample.E[0], sample.E[1], sample.E[2], 0)), C1fac = _mm_div_ps(_mm_mul_ps(zr, _mm_add_ps(sigmaTr, _mm_div_ps(one, dr))), drSqr), C2fac = _mm_div_ps(_mm_mul_ps(zv, _mm_add_ps(sigmaTr, _mm_div_ps(one, dv))), dvSqr); SSEVector temp1(_mm_mul_ps(dr, sigmaTr)), temp2(_mm_mul_ps(dv, sigmaTr)); const __m128 exp1 = _mm_set_ps(expf(-temp1.f[3]), expf(-temp1.f[2]), expf(-temp1.f[1]), 0), exp2 = _mm_set_ps(expf(-temp2.f[3]), expf(-temp2.f[2]), expf(-temp2.f[1]), 0); result.ps = _mm_add_ps(result.ps, _mm_mul_ps(factor, _mm_add_ps( _mm_mul_ps(C1fac, exp1), _mm_mul_ps(C2fac, exp2)))); } Spectrum getResult() { Spectrum value; for (int i=0; i<3; ++i) value[i] = result.f[3-i]; return value; } __m128 zr, zv, zrSqr, zvSqr, sigmaTr; SSEVector result; #endif int count; Float Fdt; Point p; }; /** * Computes the fluence and vector irradiance at a given point inside the medium */ struct RadianceQuery { inline RadianceQuery(const Spectrum &zr, const Spectrum &zv, const Spectrum &sigmaTr, const Spectrum &mfp, const Spectrum &D, Float Fdt, const Point &p, const Normal &n) : zr(zr), zv(zv), sigmaTr(sigmaTr), mfp(mfp), D(D), Fdt(Fdt), p(p) { frame = Frame(n); } inline void operator()(const IrradianceSample &sample) { Point sampleP = sample.p; Vector diff = frame.toLocal(sampleP - p); const Spectrum weight = Spectrum(0.25f * INV_PI) * sample.E * sample.area * Fdt; for (int i=0; i irrOctreeMutex = new Mutex(); static int irrOctreeIndex = 0; /** * Subsurface scattering integrator using Jensen's fast hierarchical * dipole approximation scheme. * * ("A Rapid Hierarhical Rendering Technique for Translucent * Materials" by Herik Wann Jensen and Juan Buhler, in SIGGRAPH 02) */ class IsotropicDipole : public Subsurface { public: IsotropicDipole(const Properties &props) : Subsurface(props) { irrOctreeMutex->lock(); m_octreeIndex = irrOctreeIndex++; irrOctreeMutex->unlock(); /* Multiplicative factor, which can be used to adjust the number of irradiance samples */ m_sampleMultiplier = props.getFloat("sampleMultiplier", 2.0f); /* Error threshold - lower means better quality */ m_minDelta= props.getFloat("quality", 0.1f); /* Max. depth of the created octree */ m_maxDepth = props.getInteger("maxDepth", 40); /* Multiplicative factor for the subsurface term - can be used to remove this contribution completely, making it possible to use this integrator for other interesting things.. */ m_ssFactor = props.getSpectrum("ssFactor", Spectrum(1.0f)); m_maxDepth = props.getInteger("maxDepth", 40); /* Asymmetry parameter of the phase function */ m_g = props.getFloat("g", 0); m_ready = false; m_octreeResID = -1; } IsotropicDipole(Stream *stream, InstanceManager *manager) : Subsurface(stream, manager) { m_ssFactor = Spectrum(stream); m_g = stream->readFloat(); m_sampleMultiplier = stream->readFloat(); m_minDelta = stream->readFloat(); m_maxDepth = stream->readInt(); m_octreeIndex = stream->readInt(); m_ready = false; m_octreeResID = -1; configure(); } virtual ~IsotropicDipole() { if (m_octreeResID != -1) Scheduler::getInstance()->unregisterResource(m_octreeResID); } void bindUsedResources(ParallelProcess *proc) const { if (m_octreeResID != -1) proc->bindResource(formatString("irrOctree%i", m_octreeIndex), m_octreeResID); } void serialize(Stream *stream, InstanceManager *manager) const { Subsurface::serialize(stream, manager); m_ssFactor.serialize(stream); stream->writeFloat(m_g); stream->writeFloat(m_sampleMultiplier); stream->writeFloat(m_minDelta); stream->writeInt(m_maxDepth); stream->writeInt(m_octreeIndex); } Spectrum Lo(const Scene *scene, const Intersection &its, const Vector &d) const { if (!m_ready || m_ssFactor.isBlack()) return Spectrum(0.0f); IsotropicDipoleQuery query(m_zr, m_zv, m_sigmaTr, m_Fdt, its.p); const Normal &n = its.shFrame.n; m_octree->execute(query); if (m_eta == 1.0f) { return query.getResult() * m_ssFactor * INV_PI; } else { Float Ft = 1.0f - fresnel(absDot(n, d)); return query.getResult() * m_ssFactor * INV_PI * (Ft / m_Fdr); } } void configure() { m_sigmaSPrime = m_sigmaS * (1-m_g); m_sigmaTPrime = m_sigmaSPrime + m_sigmaA; /* Mean-free path (avg. distance traveled through the medium) */ m_mfp = Spectrum(1.0f) / m_sigmaTPrime; /* Also find the smallest mean-free path for all wavelengths */ m_minMFP = std::numeric_limits::max(); for (int lambda=0; lambdagetIntegrator()->getClass() ->derivesFrom(SampleIntegrator::m_theClass)) { Log(EError, "The dipole subsurface integrator requires " "a sampling-based surface integrator!"); } m_octree = new IrradianceOctree(m_maxDepth, m_minDelta, scene->getKDTree()->getAABB()); Float sa = 0; for (std::vector::iterator it = m_shapes.begin(); it != m_shapes.end(); ++it) sa += (*it)->getSurfaceArea(); size_t sampleCount = (size_t) std::ceil(sa / (M_PI * m_minMFP * m_minMFP) * m_sampleMultiplier); ref sched = Scheduler::getInstance(); /* This could be a bit more elegant.. - inform the irradiance sampler about the index of this subsurface integrator */ std::vector ssIntegrators = scene->getSubsurfaceIntegrators(); int index = -1; for (size_t i=0; i proc = new IrradianceSamplingProcess( sampleCount, (size_t) std::ceil(sampleCount/100.0f), index, job); proc->bindResource("scene", sceneResID); scene->bindUsedResources(proc); sched->schedule(proc); sched->wait(proc); const IrradianceRecordVector &results = *proc->getSamples(); for (size_t i=0; iaddSample(results[i]); m_octree->preprocess(); m_octreeResID = Scheduler::getInstance()->registerResource(m_octree); m_ready = true; } void wakeup(std::map ¶ms) { std::string octreeName = formatString("irrOctree%i", m_octreeIndex); if (!m_octree.get() && params.find(octreeName) != params.end()) { m_octree = static_cast(params[octreeName]); m_ready = true; } } MTS_DECLARE_CLASS() private: Float m_minMFP, m_sampleMultiplier; Float m_Fdr, m_Fdt, m_A, m_minDelta, m_g; Spectrum m_mfp, m_sigmaTr, m_zr, m_zv, m_alphaPrime; Spectrum m_sigmaSPrime, m_sigmaTPrime, m_D, m_ssFactor; ref m_octree; int m_octreeResID, m_octreeIndex; int m_maxDepth; bool m_ready, m_requireSample; }; MTS_IMPLEMENT_CLASS_S(IsotropicDipole, false, Subsurface) MTS_EXPORT_PLUGIN(IsotropicDipole, "Isotropic dipole model"); MTS_NAMESPACE_END