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