/* 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 #include MTS_NAMESPACE_BEGIN /*! \plugin{mixturebsdf}{Mixture material} * \order{15} * \parameters{ * \parameter{weights}{\String}{A comma-separated list of BSDF weights} * \parameter{\Unnamed}{\BSDF}{Multiple BSDF instances that should be * mixed according to the specified weights} * } * \renderings{ * \medrendering{Smooth glass}{bsdf_mixturebsdf_smooth} * \medrendering{Rough glass}{bsdf_mixturebsdf_rough} * \medrendering{An mixture of 70% smooth glass and 30% rough glass * results in a more realistic smooth material with imperfections * (\lstref{mixture-example})}{bsdf_mixturebsdf_result} * } * * This plugin implements a ``mixture'' material, which represents * linear combinations of multiple BSDF instances. Any surface scattering * model in Mitsuba (be it smooth, rough, reflecting, or transmitting) can * be mixed with others in this manner to synthesize new models. There * is no limit on how many models can be mixed, but their combination * weights must be non-negative and sum to a value of one or less to ensure * energy balance. When they sum to less than one, the material will * absorb a proportional amount of the incident illlumination. * * \vspace{4mm} * \begin{xml}[caption={A material definition for a mixture of 70% smooth * and 30% rough glass}, * label=lst:mixture-example] * * * * * * * * * * \end{xml} */ class MixtureBSDF : public BSDF { public: MixtureBSDF(const Properties &props) : BSDF(props) { /* Parse the weight parameter */ std::vector weights = tokenize(props.getString("weights", ""), " ,;"); if (weights.size() == 0) Log(EError, "No weights were supplied!"); m_weights.resize(weights.size()); char *end_ptr = NULL; for (size_t i=0; ireadSize(); m_weights.resize(bsdfCount); for (size_t i=0; ireadFloat(); BSDF *bsdf = static_cast(manager->getInstance(stream)); bsdf->incRef(); m_bsdfs.push_back(bsdf); } configure(); } virtual ~MixtureBSDF() { for (size_t i=0; idecRef(); } void serialize(Stream *stream, InstanceManager *manager) const { BSDF::serialize(stream, manager); stream->writeSize(m_bsdfs.size()); for (size_t i=0; iwriteFloat(m_weights[i]); manager->serialize(stream, m_bsdfs[i]); } } void configure() { m_usesRayDifferentials = false; size_t componentCount = 0; if (m_bsdfs.size() != m_weights.size()) Log(EError, "BSDF count mismatch: " SIZE_T_FMT " bsdfs, but specified " SIZE_T_FMT " weights", m_bsdfs.size(), m_bsdfs.size()); Float totalWeight = 0; for (size_t i=0; i 1) { std::ostringstream oss; Float scale = 1.0f / totalWeight; oss << "The BSDF" << endl << toString() << endl << "potentially violates energy conservation, since the weights " << "sum to " << totalWeight << ", which is greater than one! " << "They will be re-scaled to avoid potential issues. Specify " << "the parameter ensureEnergyConservation=false to prevent " << "this from happening."; Log(EWarn, "%s", oss.str().c_str()); for (size_t i=0; igetComponentCount(); m_pdf = DiscreteDistribution(m_bsdfs.size()); m_components.reserve(componentCount); m_components.clear(); m_indices.reserve(componentCount); m_indices.clear(); m_offsets.reserve(m_bsdfs.size()); m_offsets.clear(); int offset = 0; for (size_t i=0; igetComponentCount(); ++j) { int componentType = bsdf->getType(j); m_components.push_back(componentType); m_indices.push_back(std::make_pair((int) i, j)); } offset += bsdf->getComponentCount(); m_usesRayDifferentials |= bsdf->usesRayDifferentials(); m_pdf.append(m_weights[i]); } m_pdf.normalize(); BSDF::configure(); } Spectrum eval(const BSDFSamplingRecord &bRec, EMeasure measure) const { Spectrum result(0.0f); if (bRec.component == -1) { for (size_t i=0; ieval(bRec, measure) * m_weights[i]; } else { /* Pick out an individual component */ int idx = m_indices[bRec.component].first; BSDFSamplingRecord bRec2(bRec); bRec2.component = m_indices[bRec.component].second; return m_bsdfs[idx]->eval(bRec2, measure) * m_weights[idx]; } return result; } Float pdf(const BSDFSamplingRecord &bRec, EMeasure measure) const { Float result = 0.0f; if (bRec.component == -1) { for (size_t i=0; ipdf(bRec, measure) * m_pdf[i]; } else { /* Pick out an individual component */ int idx = m_indices[bRec.component].first; BSDFSamplingRecord bRec2(bRec); bRec2.component = m_indices[bRec.component].second; return m_bsdfs[idx]->pdf(bRec2, measure); } return result; } Spectrum sample(BSDFSamplingRecord &bRec, const Point2 &_sample) const { Point2 sample(_sample); if (bRec.component == -1) { /* Choose a component based on the normalized weights */ size_t entry = m_pdf.sampleReuse(sample.x); Float pdf; Spectrum result = m_bsdfs[entry]->sample(bRec, pdf, sample); if (result.isZero()) // sampling failed return result; result *= m_weights[entry] * pdf; pdf *= m_pdf[entry]; EMeasure measure = BSDF::getMeasure(bRec.sampledType); for (size_t i=0; ipdf(bRec, measure) * m_pdf[i]; result += m_bsdfs[i]->eval(bRec, measure) * m_weights[i]; } bRec.sampledComponent += m_offsets[entry]; return result / pdf; } else { /* Pick out an individual component */ int requestedComponent = bRec.component; int bsdfIndex = m_indices[requestedComponent].first; bRec.component = m_indices[requestedComponent].second; Spectrum result = m_bsdfs[bsdfIndex]->sample(bRec, sample) * m_weights[bsdfIndex]; bRec.component = bRec.sampledComponent = requestedComponent; return result; } } Spectrum sample(BSDFSamplingRecord &bRec, Float &pdf, const Point2 &_sample) const { Point2 sample(_sample); if (bRec.component == -1) { /* Choose a component based on the normalized weights */ size_t entry = m_pdf.sampleReuse(sample.x); Spectrum result = m_bsdfs[entry]->sample(bRec, pdf, sample); if (result.isZero()) // sampling failed return result; result *= m_weights[entry] * pdf; pdf *= m_pdf[entry]; EMeasure measure = BSDF::getMeasure(bRec.sampledType); for (size_t i=0; ipdf(bRec, measure) * m_pdf[i]; result += m_bsdfs[i]->eval(bRec, measure) * m_weights[i]; } bRec.sampledComponent += m_offsets[entry]; return result/pdf; } else { /* Pick out an individual component */ int requestedComponent = bRec.component; int bsdfIndex = m_indices[requestedComponent].first; bRec.component = m_indices[requestedComponent].second; Spectrum result = m_bsdfs[bsdfIndex]->sample(bRec, pdf, sample) * m_weights[bsdfIndex]; bRec.component = bRec.sampledComponent = requestedComponent; return result; } } void addChild(const std::string &name, ConfigurableObject *child) { if (child->getClass()->derivesFrom(MTS_CLASS(BSDF))) { BSDF *bsdf = static_cast(child); m_bsdfs.push_back(bsdf); bsdf->incRef(); } else { BSDF::addChild(name, child); } } Float getRoughness(const Intersection &its, int component) const { int bsdfIndex = m_indices[component].first; component = m_indices[component].second; return m_bsdfs[bsdfIndex]->getRoughness(its, component); } std::string toString() const { std::ostringstream oss; oss << "MixtureBSDF[" << endl << " id = \"" << getID() << "\"," << endl << " weights = {"; for (size_t i=0; itoString(), 2) << "," << endl; oss << " }" << endl << "]"; return oss.str(); } Shader *createShader(Renderer *renderer) const; MTS_DECLARE_CLASS() private: std::vector m_weights; std::vector > m_indices; std::vector m_offsets; std::vector m_bsdfs; DiscreteDistribution m_pdf; }; // ================ Hardware shader implementation ================ class MixtureBSDFShader : public Shader { public: MixtureBSDFShader(Renderer *renderer, const std::vector &bsdfs, const std::vector &weights) : Shader(renderer, EBSDFShader), m_bsdfs(bsdfs), m_weights(weights), m_complete(false) { m_bsdfShader.resize(bsdfs.size()); for (size_t i=0; i shader = renderer->registerShaderForResource(bsdfs[i]); if (shader) { shader->incRef(); /* At least one shader has a hardware implementation */ m_complete = true; } m_bsdfShader[i] = shader; } } bool isComplete() const { return m_complete; } void cleanup(Renderer *renderer) { for (size_t i=0; iunregisterShaderForResource(m_bsdfs[i]); if (m_bsdfShader[i]) m_bsdfShader[i]->decRef(); } m_bsdfShader.clear(); } void putDependencies(std::vector &deps) { for (size_t i=0; i &depNames) const { Assert(m_complete); int ctr = 0; for (size_t i=0; i ¶meterIDs) const { int ctr = 0; for (size_t i=0; igetParameterID(formatString("%s_weight_%i", evalName.c_str(), ctr++), false)); } } void bind(GPUProgram *program, const std::vector ¶meterIDs, int &textureUnitOffset) const { int ctr = 0; for (size_t i=0; isetParameter(parameterIDs[ctr++], m_weights[i]); } } MTS_DECLARE_CLASS() private: std::vector m_bsdfShader; const std::vector &m_bsdfs; const std::vector &m_weights; bool m_complete; }; Shader *MixtureBSDF::createShader(Renderer *renderer) const { return new MixtureBSDFShader(renderer, m_bsdfs, m_weights); } MTS_IMPLEMENT_CLASS(MixtureBSDFShader, false, Shader) MTS_IMPLEMENT_CLASS_S(MixtureBSDF, false, BSDF) MTS_EXPORT_PLUGIN(MixtureBSDF, "Mixture BSDF") MTS_NAMESPACE_END