diff --git a/src/bsdfs/SConscript b/src/bsdfs/SConscript index 976f1d0a..ac90d350 100644 --- a/src/bsdfs/SConscript +++ b/src/bsdfs/SConscript @@ -14,6 +14,7 @@ plugins += env.SharedLibrary('roughplastic', ['roughplastic.cpp']) plugins += env.SharedLibrary('twosided', ['twosided.cpp']) plugins += env.SharedLibrary('mask', ['mask.cpp']) plugins += env.SharedLibrary('mixturebsdf', ['mixturebsdf.cpp']) +plugins += env.SharedLibrary('blendbsdf', ['blendbsdf.cpp']) plugins += env.SharedLibrary('coating', ['coating.cpp']) plugins += env.SharedLibrary('roughcoating', ['roughcoating.cpp']) plugins += env.SharedLibrary('bump', ['bump.cpp']) diff --git a/src/bsdfs/blendbsdf.cpp b/src/bsdfs/blendbsdf.cpp new file mode 100644 index 00000000..7f457e05 --- /dev/null +++ b/src/bsdfs/blendbsdf.cpp @@ -0,0 +1,276 @@ +/* + This file is part of Mitsuba, a physically based rendering system. + + Copyright (c) 2007-2011 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 +#include + +MTS_NAMESPACE_BEGIN + +/*! \plugin{blendbsdf}{Blended material} + * + * \parameters{ + * \parameter{weight}{\Float\Or\Texture}{A floating point value or texture + * with values between zero and one. The extreme values zero and one activate the + * first and second nested BSDF respectively, and inbetween values + * interpolate accordingly. \default{0.5}} + * \parameter{\Unnamed}{\BSDF}{Two nested BSDF instances that should be + * mixed according to the specified blending weight} + * } + * + * This plugin implements a ``blend'' material, which represents + * linear combinations of two BSDF instances. It is conceptually very similar + * to the \pluginref{mixture} plugin. The main difference is that + * \pluginref{blendbsdf} can interpolate based on a texture. + * + * 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. + */ + +class BlendBSDF : public BSDF { +public: + BlendBSDF(const Properties &props) + : BSDF(props) { + m_weight = new ConstantFloatTexture(props.getFloat("weight", 0.5f)); + } + + BlendBSDF(Stream *stream, InstanceManager *manager) + : BSDF(stream, manager) { + m_weight = static_cast(manager->getInstance(stream)); + m_bsdfs.push_back(static_cast(manager->getInstance(stream))); + m_bsdfs.push_back(static_cast(manager->getInstance(stream))); + configure(); + } + + virtual ~BlendBSDF() { + for (size_t i=0; idecRef(); + } + + void serialize(Stream *stream, InstanceManager *manager) const { + BSDF::serialize(stream, manager); + + Assert(m_bsdfs.size() == 2); + manager->serialize(stream, m_weight.get()); + manager->serialize(stream, m_bsdfs[0]); + manager->serialize(stream, m_bsdfs[1]); + } + + void configure() { + m_usesRayDifferentials = false; + size_t componentCount = 0; + + if (m_bsdfs.size() != 2) + Log(EError, "BSDF count mismatch: expected two nested BSDF instances!"); + + for (size_t i=0; igetComponentCount(); + + 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(); + } + BSDF::configure(); + } + + Spectrum eval(const BSDFQueryRecord &bRec, EMeasure measure) const { + Float weight = std::min(1.0f, std::max(0.0f, + m_weight->getValue(bRec.its).average())); + + if (bRec.component == -1) { + return + m_bsdfs[0]->eval(bRec, measure) * (1-weight) + + m_bsdfs[1]->eval(bRec, measure) * weight; + } else { + /* Pick out an individual component */ + int idx = m_indices[bRec.component].first; + if (idx == 0) + weight = 1-weight; + BSDFQueryRecord bRec2(bRec); + bRec2.component = m_indices[bRec.component].second; + return m_bsdfs[idx]->eval(bRec2, measure) * weight; + } + } + + Float pdf(const BSDFQueryRecord &bRec, EMeasure measure) const { + Spectrum result; + + Float weight = std::min(1.0f, std::max(0.0f, + m_weight->getValue(bRec.its).average())); + + if (bRec.component == -1) { + return + m_bsdfs[0]->pdf(bRec, measure) * (1-weight) + + m_bsdfs[1]->pdf(bRec, measure) * weight; + } else { + /* Pick out an individual component */ + int idx = m_indices[bRec.component].first; + if (idx == 0) + weight = 1-weight; + BSDFQueryRecord bRec2(bRec); + bRec2.component = m_indices[bRec.component].second; + return m_bsdfs[idx]->pdf(bRec2, measure) * weight; + } + } + + Spectrum sample(BSDFQueryRecord &bRec, const Point2 &_sample) const { + Point2 sample(_sample); + + Float weights[2]; + weights[1] = std::min(1.0f, std::max(0.0f, + m_weight->getValue(bRec.its).average())); + weights[0] = 1-weights[1]; + + if (bRec.component == -1) { + size_t entry; + if (sample.x < weights[0]) { + entry = 0; sample.x /= weights[0]; + } else { + entry = 1; sample.x = (sample.x - weights[0]) / weights[1]; + } + + Float pdf; + Spectrum result = m_bsdfs[entry]->sample(bRec, pdf, sample); + if (result.isZero()) // sampling failed + return result; + + result *= weights[entry] * pdf; + pdf *= weights[entry]; + + EMeasure measure = BSDF::getMeasure(bRec.sampledType); + for (size_t i=0; ipdf(bRec, measure) * weights[i]; + result += m_bsdfs[i]->eval(bRec, measure) * 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) + * weights[bsdfIndex]; + bRec.component = bRec.sampledComponent = requestedComponent; + return result; + } + } + + Spectrum sample(BSDFQueryRecord &bRec, Float &pdf, const Point2 &_sample) const { + Point2 sample(_sample); + + Float weights[2]; + weights[1] = std::min(1.0f, std::max(0.0f, + m_weight->getValue(bRec.its).average())); + weights[0] = 1-weights[1]; + + if (bRec.component == -1) { + size_t entry; + if (sample.x < weights[0]) { + entry = 0; sample.x /= weights[0]; + } else { + entry = 1; sample.x = (sample.x - weights[0]) / weights[1]; + } + + Spectrum result = m_bsdfs[entry]->sample(bRec, pdf, sample); + if (result.isZero()) // sampling failed + return result; + + result *= weights[entry] * pdf; + pdf *= weights[entry]; + + EMeasure measure = BSDF::getMeasure(bRec.sampledType); + for (size_t i=0; ipdf(bRec, measure) * weights[i]; + result += m_bsdfs[i]->eval(bRec, measure) * 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) + * 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 if (child->getClass()->derivesFrom(MTS_CLASS(Texture)) && name == "weight") { + m_weight = static_cast(child); + } else { + BSDF::addChild(name, child); + } + } + + std::string toString() const { + std::ostringstream oss; + oss << "BlendBSDF[" << endl + << " name = \"" << getName() << "\"," << endl + << " weight = " << indent(m_weight->toString()) << endl + << " bsdfs = {" << endl; + for (size_t i=0; itoString(), 2) << "," << endl; + oss << " }" + << "]"; + return oss.str(); + } + + MTS_DECLARE_CLASS() +private: + std::vector m_bsdfs; + ref m_weight; + std::vector > m_indices; + std::vector m_offsets; +}; + + +MTS_IMPLEMENT_CLASS_S(BlendBSDF, false, BSDF) +MTS_EXPORT_PLUGIN(BlendBSDF, "Blend BSDF") +MTS_NAMESPACE_END