/* 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 . */ #include #include MTS_NAMESPACE_BEGIN /*!\plugin{directional}{Directional emitter} * \icon{emitter_directional} * \order{4} * \parameters{ * \parameter{toWorld}{\Transform\Or\Animation}{ * Specifies an optional emitter-to-world transformation. * \default{none (i.e. emitter space $=$ world space)} * } * \parameter{direction}{\Vector}{ * Alternative to \code{toWorld}: explicitly specifies * the illumination direction. Note that only one of the * two parameters can be used. * } * \parameter{irradiance}{\Spectrum}{ * Specifies the amount of power per unit area received * by a hypothetical surface normal to the specified direction * \default{1} * } * \parameter{samplingWeight}{\Float}{ * Specifies the relative amount of samples * allocated to this emitter. \default{1} * } * } * * This emitter plugin implements a distant directional source, which * radiates a specified power per unit area along a fixed direction. * By default, the emitter radiates in the direction of the postive Z axis. */ class DirectionalEmitter : public Emitter { public: DirectionalEmitter(const Properties &props) : Emitter(props) { m_type |= EDeltaDirection; m_normalIrradiance = props.getSpectrum("irradiance", Spectrum(1.0f)); if (props.hasProperty("direction")) { if (props.hasProperty("toWorld")) Log(EError, "Only one of the parameters 'direction' and 'toWorld'" "can be used at a time!"); Vector d(normalize(props.getVector("direction"))), u, unused; coordinateSystem(d, u, unused); m_worldTransform = new AnimatedTransform( Transform::lookAt(Point(0.0f), Point(d), u)); } else { if (props.getTransform("toWorld", Transform()).hasScale()) Log(EError, "Scale factors in the emitter-to-world " "transformation are not allowed!"); } } DirectionalEmitter(Stream *stream, InstanceManager *manager) : Emitter(stream, manager) { m_normalIrradiance = Spectrum(stream); m_bsphere = BSphere(stream); configure(); } void serialize(Stream *stream, InstanceManager *manager) const { Emitter::serialize(stream, manager); m_normalIrradiance.serialize(stream); m_bsphere.serialize(stream); } ref createShape(const Scene *scene) { /* Create a bounding sphere that surrounds the scene */ m_bsphere = scene->getKDTree()->getAABB().getBSphere(); m_bsphere.radius *= 1.1f; configure(); return NULL; } void configure() { Emitter::configure(); Float surfaceArea = M_PI * m_bsphere.radius * m_bsphere.radius; m_invSurfaceArea = 1.0f / surfaceArea; m_power = m_normalIrradiance * surfaceArea; } Spectrum samplePosition(PositionSamplingRecord &pRec, const Point2 &sample, const Point2 *extra) const { const Transform &trafo = m_worldTransform->eval(pRec.time); Point2 p = Warp::squareToUniformDiskConcentric(sample); Vector perpOffset = trafo(Vector(p.x, p.y, 0) * m_bsphere.radius); Vector d = trafo(Vector(0, 0, 1)); pRec.p = m_bsphere.center - d*m_bsphere.radius + perpOffset; pRec.n = d; pRec.pdf = m_invSurfaceArea; pRec.measure = EArea; return m_power; } Spectrum evalPosition(const PositionSamplingRecord &pRec) const { return (pRec.measure == EArea) ? m_normalIrradiance : Spectrum(0.0f); } Float pdfPosition(const PositionSamplingRecord &pRec) const { return (pRec.measure == EArea) ? m_invSurfaceArea : 0.0f; } Spectrum sampleDirection(DirectionSamplingRecord &dRec, PositionSamplingRecord &pRec, const Point2 &sample, const Point2 *extra) const { dRec.d = pRec.n; dRec.pdf = 1.0f; dRec.measure = EDiscrete; return Spectrum(1.0f); } Float pdfDirection(const DirectionSamplingRecord &dRec, const PositionSamplingRecord &pRec) const { return (dRec.measure == EDiscrete) ? 1.0f : 0.0f; } Spectrum evalDirection(const DirectionSamplingRecord &dRec, const PositionSamplingRecord &pRec) const { return Spectrum((dRec.measure == EDiscrete) ? 1.0f : 0.0f); } Spectrum sampleRay(Ray &ray, const Point2 &spatialSample, const Point2 &directionalSample, Float time) const { const Transform &trafo = m_worldTransform->eval(time); Point2 p = Warp::squareToUniformDiskConcentric(spatialSample); Vector perpOffset = trafo(Vector(p.x, p.y, 0) * m_bsphere.radius); Vector d = trafo(Vector(0, 0, 1)); ray.setOrigin(m_bsphere.center - d*m_bsphere.radius + perpOffset); ray.setDirection(d); ray.setTime(time); return m_power; } Spectrum sampleDirect(DirectSamplingRecord &dRec, const Point2 &sample) const { const Transform &trafo = m_worldTransform->eval(dRec.time); Vector d = trafo(Vector(0,0,1)); Point diskCenter = m_bsphere.center - d*m_bsphere.radius; Float distance = dot(dRec.ref - diskCenter, d); if (distance < 0) { /* This can happen when doing bidirectional renderings involving environment maps and directional sources. Just return zero */ return Spectrum(0.0f); } dRec.p = dRec.ref - distance * d; dRec.d = -d; dRec.n = Normal(d); dRec.dist = distance; dRec.pdf = 1.0f; dRec.measure = EDiscrete; return m_normalIrradiance; } Float pdfDirect(const DirectSamplingRecord &dRec) const { return dRec.measure == EDiscrete ? 1.0f : 0.0f; } AABB getAABB() const { return AABB(); } Shader *createShader(Renderer *renderer) const; std::string toString() const { std::ostringstream oss; oss << "DirectionalEmitter[" << endl << " normalIrradiance = " << m_normalIrradiance.toString() << "," << endl << " samplingWeight = " << m_samplingWeight << "," << endl << " worldTransform = " << indent(m_worldTransform.toString()) << "," << endl << " medium = " << indent(m_medium.toString()) << endl << "]"; return oss.str(); } MTS_DECLARE_CLASS() private: Spectrum m_normalIrradiance, m_power; BSphere m_bsphere; Float m_invSurfaceArea; }; // ================ Hardware shader implementation ================ class DirectionalEmitterShader : public Shader { public: DirectionalEmitterShader(Renderer *renderer) : Shader(renderer, EEmitterShader) { } void generateCode(std::ostringstream &oss, const std::string &evalName, const std::vector &depNames) const { oss << "vec3 " << evalName << "_dir(vec3 wo) {" << endl << " return vec3(1.0);" << endl << "}" << endl; } MTS_DECLARE_CLASS() }; Shader *DirectionalEmitter::createShader(Renderer *renderer) const { return new DirectionalEmitterShader(renderer); } MTS_IMPLEMENT_CLASS(DirectionalEmitterShader, false, Shader) MTS_IMPLEMENT_CLASS_S(DirectionalEmitter, false, Emitter) MTS_EXPORT_PLUGIN(DirectionalEmitter, "Directional emitter"); MTS_NAMESPACE_END