/*
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