sun/sunsky: allow turning the sun into a directional light source (optional)

metadata
Wenzel Jakob 2012-11-01 20:32:44 -04:00
parent 76b9bd0841
commit 28120678ac
2 changed files with 83 additions and 32 deletions

View File

@ -65,6 +65,10 @@ MTS_NAMESPACE_BEGIN
* This parameter can be used to scale the the amount of illumination
* emitted by the sun emitter. \default{1}
* }
* \parameter{sunRadiusScale}{\Float}{
* Scale factor to adjust the radius of the sun, while preserving its power.
* Set to \code{0} to turn it into a directional light source.
* }
* \parameter{samplingWeight}{\Float}{
* Specifies the relative amount of samples
* allocated to this emitter. \default{1}
@ -74,7 +78,6 @@ MTS_NAMESPACE_BEGIN
* Preetham et al. \cite{Preetham1999Practical}. Using the provided position
* and time information (see \pluginref{sky} for details), it can determine the
* position of the sun as seen from the position of the observer.
*
* The radiance arriving at the earth surface is then found based on the spectral
* emission profile of the sun and the extinction cross-section of the
* atmosphere (which depends on the \code{turbidity} and the zenith angle of the sun).
@ -146,6 +149,23 @@ public:
Emitter *getElement(size_t i) {
if (i != 0)
return NULL;
if (m_sunRadiusScale == 0) {
Properties props("directional");
const Transform &trafo = m_worldTransform->eval(0);
props.setVector("direction", -trafo(m_sunDir));
props.setFloat("samplingWeight", m_samplingWeight);
props.setSpectrum("irradiance", m_radiance * m_solidAngle);
Emitter *emitter = static_cast<Emitter *>(
PluginManager::getInstance()->createObject(
MTS_CLASS(Emitter), props));
emitter->configure();
return emitter;
}
/* Rasterizing the sphere to an environment map and checking the
individual pixels for coverage (which is what Mitsuba 0.3.0 did)
was slow and not very effective; for instance the power varied

View File

@ -74,16 +74,22 @@ MTS_NAMESPACE_BEGIN
* This parameter can be used to separately scale the the amount of illumination
* emitted by the sky.\default{1}
* }
* \parameter{sunRadiusScale}{\Float}{
* Scale factor to adjust the radius of the sun, while preserving its power.
* Set to \code{0} to turn it into a directional light source.
* }
* }
* \vspace{-3mm}
*
* \renderings{
* \medrendering{\pluginref{sky} emitter}{emitter_sunsky_sky}
* \medrendering{\pluginref{sun} emitter}{emitter_sunsky_sun}
* \medrendering{\pluginref{sunsky} emitter}{emitter_sunsky_sunsky}
* \vspace{-2mm}
* \caption{A coated rough copper test ball lit with the three
* provided daylight illumination models}
* }
* \vspace{5mm}
* \vspace{1mm}
* This convenience plugin has the sole purpose of instantiating
* \pluginref{sun} and \pluginref{sky} and merging them into a joint
* environment map. Please refer to these plugins individually for more
@ -95,7 +101,8 @@ public:
: Emitter(props) {
Float scale = props.getFloat("scale", 1.0f),
sunScale = props.getFloat("sunScale", scale),
skyScale = props.getFloat("skyScale", scale);
skyScale = props.getFloat("skyScale", scale),
sunRadiusScale = props.getFloat("sunRadiusScale", 1.0f);
const Transform &trafo = m_worldTransform->eval(0);
@ -159,39 +166,53 @@ public:
props.getFloat("turbidity", 3.0f)) * sunScale;
sun.elevation *= props.getFloat("stretch", 1.0f);
Frame sunFrame = Frame(toSphere(sun));
Float theta = degToRad(SUN_APP_RADIUS * 0.5f);
size_t pixelCount = resolution*resolution/2;
Float cosTheta = std::cos(theta * props.getFloat("sunRadiusScale", 1.0f));
if (sunRadiusScale == 0) {
Float solidAngle = 2 * M_PI * (1 - std::cos(theta));
Properties props("directional");
props.setVector("direction", -trafo(sunFrame.n));
props.setFloat("samplingWeight", m_samplingWeight);
props.setSpectrum("irradiance", sunRadiance * solidAngle);
/* Ratio of the sphere that is covered by the sun */
Float coveredPortion = 0.5f * (1 - cosTheta);
m_dirEmitter = static_cast<Emitter *>(
PluginManager::getInstance()->createObject(
MTS_CLASS(Emitter), props));
} else {
size_t pixelCount = resolution*resolution/2;
Float cosTheta = std::cos(theta * sunRadiusScale);
/* Approx. number of samples that need to be generated,
be very conservative */
size_t nSamples = (size_t) std::max((Float) 100,
(pixelCount * coveredPortion * 1000));
/* Ratio of the sphere that is covered by the sun */
Float coveredPortion = 0.5f * (1 - cosTheta);
factor = Point2(bitmap->getWidth() / (2*M_PI),
bitmap->getHeight() / M_PI);
/* Approx. number of samples that need to be generated,
be very conservative */
size_t nSamples = (size_t) std::max((Float) 100,
(pixelCount * coveredPortion * 1000));
Spectrum value =
sunRadiance * (2 * M_PI * (1-std::cos(theta))) *
(bitmap->getWidth() * bitmap->getHeight())
/ (2.0f * M_PI * M_PI * (Float) nSamples);
factor = Point2(bitmap->getWidth() / (2*M_PI),
bitmap->getHeight() / M_PI);
for (size_t i=0; i<nSamples; ++i) {
Vector dir = sunFrame.toWorld(
Warp::squareToUniformCone(cosTheta, sample02(i)));
Spectrum value =
sunRadiance * (2 * M_PI * (1-std::cos(theta))) *
(bitmap->getWidth() * bitmap->getHeight())
/ (2.0f * M_PI * M_PI * (Float) nSamples);
Float sinTheta = math::safe_sqrt(1-dir.y*dir.y);
SphericalCoordinates sphCoords = fromSphere(dir);
for (size_t i=0; i<nSamples; ++i) {
Vector dir = sunFrame.toWorld(
Warp::squareToUniformCone(cosTheta, sample02(i)));
Point2i pos(
std::min(std::max(0, (int) (sphCoords.azimuth * factor.x)), bitmap->getWidth()-1),
std::min(std::max(0, (int) (sphCoords.elevation * factor.y)), bitmap->getHeight()-1));
Float sinTheta = math::safe_sqrt(1-dir.y*dir.y);
SphericalCoordinates sphCoords = fromSphere(dir);
Point2i pos(
std::min(std::max(0, (int) (sphCoords.azimuth * factor.x)), bitmap->getWidth()-1),
std::min(std::max(0, (int) (sphCoords.elevation * factor.y)), bitmap->getHeight()-1));
data[pos.x + pos.y * bitmap->getWidth()] += value / std::max((Float) 1e-3f, sinTheta);
}
data[pos.x + pos.y * bitmap->getWidth()] += value / std::max((Float) 1e-3f, sinTheta);
}
Log(EDebug, "Done (took %i ms)", timer->getMilliseconds());
@ -204,7 +225,7 @@ public:
envProps.setData("bitmap", bitmapData);
envProps.setTransform("toWorld", trafo);
envProps.setFloat("samplingWeight", m_samplingWeight);
m_emitter = static_cast<Emitter *>(
m_envEmitter = static_cast<Emitter *>(
PluginManager::getInstance()->createObject(
MTS_CLASS(Emitter), envProps));
@ -217,17 +238,24 @@ public:
SunSkyEmitter(Stream *stream, InstanceManager *manager)
: Emitter(stream, manager) {
m_emitter = static_cast<Emitter *>(manager->getInstance(stream));
m_envEmitter = static_cast<Emitter *>(manager->getInstance(stream));
if (stream->readBool())
m_dirEmitter = static_cast<Emitter *>(manager->getInstance(stream));
}
void serialize(Stream *stream, InstanceManager *manager) const {
Emitter::serialize(stream, manager);
manager->serialize(stream, m_emitter.get());
manager->serialize(stream, m_envEmitter.get());
stream->writeBool(m_dirEmitter.get() != NULL);
if (m_dirEmitter.get())
manager->serialize(stream, m_dirEmitter.get());
}
void configure() {
Emitter::configure();
m_emitter->configure();
m_envEmitter->configure();
if (m_dirEmitter)
m_dirEmitter->configure();
}
bool isCompound() const {
@ -240,14 +268,17 @@ public:
Emitter *getElement(size_t i) {
if (i == 0)
return m_emitter;
return m_envEmitter;
else if (i == 1)
return m_dirEmitter;
else
return NULL;
}
MTS_DECLARE_CLASS()
private:
ref<Emitter> m_emitter;
ref<Emitter> m_dirEmitter;
ref<Emitter> m_envEmitter;
};
MTS_IMPLEMENT_CLASS_S(SunSkyEmitter, false, Emitter)