sky luminaire improvements
After Width: | Height: | Size: 47 KiB |
After Width: | Height: | Size: 46 KiB |
After Width: | Height: | Size: 46 KiB |
After Width: | Height: | Size: 45 KiB |
After Width: | Height: | Size: 46 KiB |
After Width: | Height: | Size: 46 KiB |
After Width: | Height: | Size: 47 KiB |
After Width: | Height: | Size: 46 KiB |
After Width: | Height: | Size: 55 KiB |
After Width: | Height: | Size: 51 KiB |
After Width: | Height: | Size: 53 KiB |
After Width: | Height: | Size: 56 KiB |
After Width: | Height: | Size: 59 KiB |
After Width: | Height: | Size: 61 KiB |
After Width: | Height: | Size: 63 KiB |
After Width: | Height: | Size: 65 KiB |
|
@ -67,6 +67,7 @@
|
||||||
\newcommand{\medrendering}[2]{ \subfloat[#1]{\fbox{\includegraphics[width=0.3\textwidth]{images/#2}}}\hfill}
|
\newcommand{\medrendering}[2]{ \subfloat[#1]{\fbox{\includegraphics[width=0.3\textwidth]{images/#2}}}\hfill}
|
||||||
\newcommand{\unframedmedrendering}[2]{\subfloat[#1]{\includegraphics[width=0.3\textwidth]{images/#2}}\hfill}
|
\newcommand{\unframedmedrendering}[2]{\subfloat[#1]{\includegraphics[width=0.3\textwidth]{images/#2}}\hfill}
|
||||||
\newcommand{\smallrendering}[2]{ \subfloat[#1]{\fbox{\includegraphics[width=0.2\textwidth]{images/#2}}}\hfill}
|
\newcommand{\smallrendering}[2]{ \subfloat[#1]{\fbox{\includegraphics[width=0.2\textwidth]{images/#2}}}\hfill}
|
||||||
|
\newcommand{\tinyrendering}[2]{ \subfloat[#1]{\includegraphics[width=0.125\textwidth]{images/#2}}}
|
||||||
|
|
||||||
\newcommand{\parameter}[3]{
|
\newcommand{\parameter}[3]{
|
||||||
\otoprule
|
\otoprule
|
||||||
|
|
|
@ -282,11 +282,11 @@ void Scene::initialize() {
|
||||||
|
|
||||||
if (!m_luminairePDF.isReady()) {
|
if (!m_luminairePDF.isReady()) {
|
||||||
if (m_luminaires.size() == 0) {
|
if (m_luminaires.size() == 0) {
|
||||||
Log(EWarn, "No luminaires found -- adding a constant environment source");
|
Log(EWarn, "No luminaires found -- adding a sky luminaire");
|
||||||
Properties constantProps("constant");
|
Properties skyPropsProps("sky");
|
||||||
constantProps.setSpectrum("intensity", Spectrum(0.9f));
|
skyPropsProps.setFloat("scale", 0.03f);
|
||||||
ref<Luminaire> luminaire = static_cast<Luminaire *>(
|
ref<Luminaire> luminaire = static_cast<Luminaire *>(
|
||||||
PluginManager::getInstance()->createObject(MTS_CLASS(Luminaire), constantProps));
|
PluginManager::getInstance()->createObject(MTS_CLASS(Luminaire), skyPropsProps));
|
||||||
addChild("", luminaire);
|
addChild("", luminaire);
|
||||||
luminaire->configure();
|
luminaire->configure();
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,10 +32,10 @@ MTS_NAMESPACE_BEGIN
|
||||||
* `haze') in the atmosphere. Smaller values ($\sim 2$) produce a
|
* `haze') in the atmosphere. Smaller values ($\sim 2$) produce a
|
||||||
* clear blue sky, larger values ($\sim 8$) lead to an overcast sky,
|
* clear blue sky, larger values ($\sim 8$) lead to an overcast sky,
|
||||||
* and a very high values ($\sim 20$) cause a color shift towards
|
* and a very high values ($\sim 20$) cause a color shift towards
|
||||||
* orange and red. \default{2}
|
* orange and red. \default{3}
|
||||||
* }
|
* }
|
||||||
* \parameter{day}{\Integer}{Solar day used to compute the sun's position.
|
* \parameter{day}{\Integer}{Solar day used to compute the sun's position.
|
||||||
* Must be in the range 1 to 365. \default{180}}
|
* Must be in the range between 1 and 365. \default{180}}
|
||||||
* \parameter{time}{\Float}{Fractional time used to compute the sun's
|
* \parameter{time}{\Float}{Fractional time used to compute the sun's
|
||||||
* position. A time of 4:15 PM corresponds to 16.25. \default{15.00}}
|
* position. A time of 4:15 PM corresponds to 16.25. \default{15.00}}
|
||||||
* \parameter{latitude, longitude}{\Float}{
|
* \parameter{latitude, longitude}{\Float}{
|
||||||
|
@ -48,10 +48,10 @@ MTS_NAMESPACE_BEGIN
|
||||||
* the sun's position \default{135 --- Japan standard time}
|
* the sun's position \default{135 --- Japan standard time}
|
||||||
* }
|
* }
|
||||||
* \parameter{sunDirection}{\Vector}{Allows to manually
|
* \parameter{sunDirection}{\Vector}{Allows to manually
|
||||||
* override the sun direction. In that case, all parameters
|
* override the sun direction in world space. When this value
|
||||||
* pertaining to the computation of this direction (\code{day,
|
* is provided, parameters pertaining to the computation
|
||||||
* time, latitude, longitude,} and \code{standardMeridian})
|
* of the sun direction (\code{day, time, latitude, longitude,}
|
||||||
* become unnecessary.
|
* and \code{standardMeridian}) are unnecessary. \default{none}
|
||||||
* }
|
* }
|
||||||
* \parameter{extend}{\Boolean}{
|
* \parameter{extend}{\Boolean}{
|
||||||
* Extend luminaire below the horizon? \default{\code{false}}
|
* Extend luminaire below the horizon? \default{\code{false}}
|
||||||
|
@ -69,10 +69,17 @@ MTS_NAMESPACE_BEGIN
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* \renderings{
|
* \renderings{
|
||||||
* \rendering{Homogeneous reflectance, see \lstref{diffuse-uniform}}
|
* \tinyrendering{6AM}{preetham_06}
|
||||||
* {bsdf_diffuse_plain}
|
* \tinyrendering{8AM}{preetham_08}
|
||||||
* \rendering{Textured reflectance, see \lstref{diffuse-textured}}
|
* \tinyrendering{10AM}{preetham_10}
|
||||||
* {bsdf_diffuse_textured}
|
* \tinyrendering{12PM}{preetham_12}
|
||||||
|
* \tinyrendering{2PM}{preetham_14}
|
||||||
|
* \tinyrendering{4PM}{preetham_16}
|
||||||
|
* \tinyrendering{6PM}{preetham_18}
|
||||||
|
* \tinyrendering{8PM}{preetham_20}\hfill
|
||||||
|
* \vspace{-3mm}
|
||||||
|
* \caption{Time series with the default settings (visualized by
|
||||||
|
* projecting the sky onto a disk)}
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* This plugin implements the physically-based skylight model proposed by
|
* This plugin implements the physically-based skylight model proposed by
|
||||||
|
@ -82,26 +89,45 @@ MTS_NAMESPACE_BEGIN
|
||||||
* of the earth.
|
* of the earth.
|
||||||
*
|
*
|
||||||
* Numerous parameters allow changing the both the position on Earth, as
|
* Numerous parameters allow changing the both the position on Earth, as
|
||||||
* well as the time of observation. These are used to compute the sun's
|
* well as the time of observation. These are used to compute the sun
|
||||||
* position which, together with \code{turbidity}, constitutes the main
|
* direction which, together with \code{turbidity}, constitutes the main
|
||||||
* parameter of the model. If desired, the sun direction can also be
|
* parameter of the model. If desired, the sun direction can also be
|
||||||
* specified explicitly.
|
* specified manually.
|
||||||
|
*
|
||||||
|
* \renderings{
|
||||||
|
* \tinyrendering{2}{preetham_turb_2}
|
||||||
|
* \tinyrendering{3}{preetham_turb_3}
|
||||||
|
* \tinyrendering{4}{preetham_turb_4}
|
||||||
|
* \tinyrendering{5}{preetham_turb_5}
|
||||||
|
* \tinyrendering{6}{preetham_turb_6}
|
||||||
|
* \tinyrendering{7}{preetham_turb_7}
|
||||||
|
* \tinyrendering{8}{preetham_turb_8}
|
||||||
|
* \tinyrendering{9}{preetham_turb_9}
|
||||||
|
* \vspace{-3mm}
|
||||||
|
* \caption{Sky light for different turbidity values (fixed time \& location)}
|
||||||
|
* }
|
||||||
*
|
*
|
||||||
* \emph{Turbidity}, the other important parameter, specifies the amount of
|
* \emph{Turbidity}, the other important parameter, specifies the amount of
|
||||||
* atmospheric extinction due to larger particles ($t_l$), as opposed to
|
* atmospheric extinction due to larger particles ($t_l$), as opposed to
|
||||||
* molecules ($t_m$). Lower values correspond to a clear sky, and higher values
|
* molecules ($t_m$). Lower values correspond to a clear sky, and higher values
|
||||||
* produce illumination resembling that of a hazy, overcast sky. Formally,
|
* produce illumination resembling that of a hazy, overcast sky. Formally,
|
||||||
* the turbidity is defined as the ratio $T=\frac{t_m+t_l}{t_m}$.
|
* the turbidity is defined as the ratio between the combined extinction
|
||||||
* Values between 1 and 30 are possible, though the model will be most
|
* cross-section and the cross-section only due to molecules, i.e.
|
||||||
* accurate for values between 2 and 6, to which it was fit using numerical
|
* $T=\frac{t_m+t_l}{t_m}$. Values between 1 and 30 are possible, though
|
||||||
* optimization.
|
* the model will be most accurate for values between 2 and 6, to which
|
||||||
|
* it was fit using numerical optimization.
|
||||||
|
|
||||||
|
* The default coordinate system of the luminaire associates the up
|
||||||
|
* direction with the $+Y$ axis. The east direction is associated with $+X$
|
||||||
|
* and the north direction is equal to $+Z$. To change this coordinate
|
||||||
|
* system, rotations can be applied using the \code{toWorld} parameter.
|
||||||
*
|
*
|
||||||
* By default, the luminaire will not emit any light below the
|
* By default, the luminaire will not emit any light below the
|
||||||
* horizon, which means that these regions will be black when they
|
* horizon, which means that these regions will be black when they
|
||||||
* are observed directly. By setting the \code{extend=true},
|
* are observed directly. By setting the \code{extend} parameter to
|
||||||
* the emitted radiance at the horizon will be extended to the entire
|
* \code{true}, the emitted radiance at the horizon will be extended to
|
||||||
* bottom hemisphere. Note that this will significantly increase
|
* the entire bottom hemisphere. Note that this will significantly
|
||||||
* the amount of illumination present in the scene.
|
* increase the amount of illumination present in the scene.
|
||||||
*
|
*
|
||||||
* For performance reasons, the implementation precomputes an environment
|
* For performance reasons, the implementation precomputes an environment
|
||||||
* map of the entire sky that is then forwarded to the \pluginref{envmap}
|
* map of the entire sky that is then forwarded to the \pluginref{envmap}
|
||||||
|
@ -114,7 +140,7 @@ MTS_NAMESPACE_BEGIN
|
||||||
* it does not extend to the night sky, where illumination from stars, galaxies,
|
* it does not extend to the night sky, where illumination from stars, galaxies,
|
||||||
* and the moon dominate. The model also currently does not handle cloudy skies.
|
* and the moon dominate. The model also currently does not handle cloudy skies.
|
||||||
* The implementation in Mitsuba is based on code by Preetham et al. It was
|
* The implementation in Mitsuba is based on code by Preetham et al. It was
|
||||||
* ported and modified by Tom Kazimiers.
|
* ported by Tom Kazimiers.
|
||||||
*/
|
*/
|
||||||
class SkyLuminaire : public Luminaire {
|
class SkyLuminaire : public Luminaire {
|
||||||
public:
|
public:
|
||||||
|
@ -127,12 +153,12 @@ public:
|
||||||
: Luminaire(props) {
|
: Luminaire(props) {
|
||||||
/* Transformation from the luminaire's local coordinates to
|
/* Transformation from the luminaire's local coordinates to
|
||||||
* world coordiantes */
|
* world coordiantes */
|
||||||
m_luminaireToWorld = Transform::rotate(Vector(1, 0, 0),-90)
|
m_luminaireToWorld =
|
||||||
* props.getTransform("toWorld", Transform());
|
props.getTransform("toWorld", Transform());
|
||||||
m_worldToLuminaire = m_luminaireToWorld.inverse();
|
m_worldToLuminaire = m_luminaireToWorld.inverse();
|
||||||
|
|
||||||
m_scale = props.getFloat("scale", Float(1.0));
|
m_scale = props.getFloat("scale", Float(1.0));
|
||||||
m_turbidity = props.getFloat("turbidity", Float(2.0));
|
m_turbidity = props.getFloat("turbidity", Float(3.0));
|
||||||
if (m_turbidity < 1 || m_turbidity > 30)
|
if (m_turbidity < 1 || m_turbidity > 30)
|
||||||
Log(EError, "The turbidity parameter must be in the range [1,30]!");
|
Log(EError, "The turbidity parameter must be in the range [1,30]!");
|
||||||
|
|
||||||
|
@ -148,8 +174,8 @@ public:
|
||||||
Log(EError, "Both the 'sunDirection' parameter and time/location "
|
Log(EError, "Both the 'sunDirection' parameter and time/location "
|
||||||
"information were provided -- only one of them can be specified at a time!");
|
"information were provided -- only one of them can be specified at a time!");
|
||||||
|
|
||||||
configureSunPosition(m_worldToLuminaire(
|
configureSunPosition(
|
||||||
props.getVector("sunDirection")));
|
props.getVector("sunDirection"));
|
||||||
} else {
|
} else {
|
||||||
Float lat = props.getFloat("latitude", 35.6894f);
|
Float lat = props.getFloat("latitude", 35.6894f);
|
||||||
Float lon = props.getFloat("longitude", 139.6917f);
|
Float lon = props.getFloat("longitude", 139.6917f);
|
||||||
|
@ -239,14 +265,13 @@ public:
|
||||||
m_perezY[4] = -0.01092f * m_turbidity + 0.05291f;
|
m_perezY[4] = -0.01092f * m_turbidity + 0.05291f;
|
||||||
|
|
||||||
int thetaBins = m_resolution, phiBins = m_resolution*2;
|
int thetaBins = m_resolution, phiBins = m_resolution*2;
|
||||||
|
|
||||||
|
#if 0
|
||||||
ref<Bitmap> bitmap = new Bitmap(phiBins, thetaBins, 128);
|
ref<Bitmap> bitmap = new Bitmap(phiBins, thetaBins, 128);
|
||||||
bitmap->clear();
|
bitmap->clear();
|
||||||
|
|
||||||
Point2 factor(M_PI / thetaBins, (2*M_PI) / phiBins);
|
Point2 factor(M_PI / thetaBins, (2*M_PI) / phiBins);
|
||||||
for (int i=0; i<thetaBins; ++i) {
|
for (int i=0; i<thetaBins; ++i) {
|
||||||
Float theta = (i+.5f)*factor.x;
|
Float theta = (i+.5f)*factor.x;
|
||||||
if (!m_extend && std::cos(theta) <= 0)
|
|
||||||
continue;
|
|
||||||
for (int j=0; j<phiBins; ++j) {
|
for (int j=0; j<phiBins; ++j) {
|
||||||
Float phi = (j+.5f)*factor.y;
|
Float phi = (j+.5f)*factor.y;
|
||||||
Spectrum s = getSkySpectralRadiance(theta, phi) * m_scale;
|
Spectrum s = getSkySpectralRadiance(theta, phi) * m_scale;
|
||||||
|
@ -258,10 +283,51 @@ public:
|
||||||
bitmap->getFloatData()[(j+i*phiBins)*4 + 3] = 1;
|
bitmap->getFloatData()[(j+i*phiBins)*4 + 3] = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
ref<Bitmap> bitmap = new Bitmap(512, 512, 128);
|
||||||
|
bitmap->clear();
|
||||||
|
for (int x=0; x<512; ++x) {
|
||||||
|
for (int y=0; y<512; ++y) {
|
||||||
|
Vector d;
|
||||||
|
d.x = -1.0f + (x + 0.5f) * 2.0f/512.0f;
|
||||||
|
d.z = -1.0f + (y + 0.5f) * 2.0f/512.0f;
|
||||||
|
Float tmp = 1-d.x*d.x - d.z*d.z;
|
||||||
|
Spectrum s(0.0f);
|
||||||
|
if (tmp > 0) {
|
||||||
|
d.y = std::sqrt(tmp);
|
||||||
|
s = Le(d);
|
||||||
|
}
|
||||||
|
Float r, g, b;
|
||||||
|
s.toLinearRGB(r, g, b);
|
||||||
|
bitmap->getFloatData()[(x+y*512)*4 + 0] = r;
|
||||||
|
bitmap->getFloatData()[(x+y*512)*4 + 1] = g;
|
||||||
|
bitmap->getFloatData()[(x+y*512)*4 + 2] = b;
|
||||||
|
bitmap->getFloatData()[(x+y*512)*4 + 3] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
ref<FileStream> fs = new FileStream("out.exr", FileStream::ETruncReadWrite);
|
ref<FileStream> fs = new FileStream("out.exr", FileStream::ETruncReadWrite);
|
||||||
bitmap->save(Bitmap::EEXR, fs);
|
bitmap->save(Bitmap::EEXR, fs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Vector toSphere(Float theta, Float phi) const {
|
||||||
|
/* Spherical-to-cartesian coordinate mapping with
|
||||||
|
theta=0 => Y=1 */
|
||||||
|
Float cosTheta = std::cos(theta), sinTheta = std::sin(theta),
|
||||||
|
cosPhi = std::cos(phi), sinPhi = std::sin(phi);
|
||||||
|
return m_luminaireToWorld(Vector(
|
||||||
|
sinTheta * sinPhi, cosTheta, -sinTheta*cosPhi));
|
||||||
|
}
|
||||||
|
|
||||||
|
Point2 fromSphere(const Vector &d) const {
|
||||||
|
Float theta = std::acos(std::max((Float) -1.0f,
|
||||||
|
std::min((Float) 1.0f, d.y)));
|
||||||
|
Float phi = std::atan2(d.x,-d.z);
|
||||||
|
if (phi < 0)
|
||||||
|
phi += 2*M_PI;
|
||||||
|
return Point2(theta, phi);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configures the position of the sun. This calculation is based on
|
* Configures the position of the sun. This calculation is based on
|
||||||
* your position on the world and time of day.
|
* your position on the world and time of day.
|
||||||
|
@ -294,13 +360,8 @@ public:
|
||||||
m_thetaS = M_PI / 2.0f - solarAltitude;
|
m_thetaS = M_PI / 2.0f - solarAltitude;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Configures the position of the sun by using a vector that points
|
|
||||||
* to the sun. It is expected, that +x = south, +y = east, +z = up.
|
|
||||||
*/
|
|
||||||
void configureSunPosition(const Vector& sunDir) {
|
void configureSunPosition(const Vector& sunDir) {
|
||||||
Vector wh = normalize(sunDir);
|
Point2 sunPos = fromSphere(normalize(m_luminaireToWorld(sunDir)));
|
||||||
Point2 sunPos = toSphericalCoordinates(wh);
|
|
||||||
m_thetaS = sunPos.x;
|
m_thetaS = sunPos.x;
|
||||||
m_phiS = sunPos.y;
|
m_phiS = sunPos.y;
|
||||||
}
|
}
|
||||||
|
@ -309,8 +370,6 @@ public:
|
||||||
/* Get the scene's bounding sphere and slightly enlarge it */
|
/* Get the scene's bounding sphere and slightly enlarge it */
|
||||||
m_bsphere = scene->getBSphere();
|
m_bsphere = scene->getBSphere();
|
||||||
m_bsphere.radius *= 1.01f;
|
m_bsphere.radius *= 1.01f;
|
||||||
m_surfaceArea = m_bsphere.radius * m_bsphere.radius * M_PI;
|
|
||||||
m_invSurfaceArea = 1/m_surfaceArea;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Spectrum getPower() const {
|
Spectrum getPower() const {
|
||||||
|
@ -322,15 +381,7 @@ public:
|
||||||
inline Spectrum Le(const Vector &direction) const {
|
inline Spectrum Le(const Vector &direction) const {
|
||||||
/* Compute sky light radiance for direction */
|
/* Compute sky light radiance for direction */
|
||||||
Vector d = normalize(m_worldToLuminaire(direction));
|
Vector d = normalize(m_worldToLuminaire(direction));
|
||||||
|
const Point2 sphCoords = fromSphere(d);
|
||||||
if (!m_extend && Frame::cosTheta(d) <= 0) {
|
|
||||||
/* If the viewing direction is below the
|
|
||||||
horizon, return black */
|
|
||||||
return Spectrum(0.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
const Point2 sphCoords = toSphericalCoordinates(d);
|
|
||||||
|
|
||||||
return getSkySpectralRadiance(sphCoords.x, sphCoords.y) * m_scale;
|
return getSkySpectralRadiance(sphCoords.x, sphCoords.y) * m_scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -456,13 +507,6 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void pdfEmission(EmissionRecord &eRec, bool delta) const {
|
void pdfEmission(EmissionRecord &eRec, bool delta) const {
|
||||||
Assert(eRec.type == EmissionRecord::ENormal);
|
|
||||||
Float dp = dot(eRec.sRec.n, eRec.d);
|
|
||||||
if (dp > 0)
|
|
||||||
eRec.pdfDir = delta ? 0.0f : INV_PI * dp;
|
|
||||||
else
|
|
||||||
eRec.pdfDir = 0;
|
|
||||||
eRec.pdfArea = delta ? 0.0f : m_invSurfaceArea;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string toString() const {
|
std::string toString() const {
|
||||||
|
@ -535,6 +579,8 @@ private:
|
||||||
* Calculates the spectral radiance of the sky in the specified directiono.
|
* Calculates the spectral radiance of the sky in the specified directiono.
|
||||||
*/
|
*/
|
||||||
Spectrum getSkySpectralRadiance(Float theta, Float phi) const {
|
Spectrum getSkySpectralRadiance(Float theta, Float phi) const {
|
||||||
|
if (!m_extend && std::cos(theta) <= 0)
|
||||||
|
return Spectrum(0.0f);
|
||||||
/* Clip directions that are extremely close to grazing (for numerical
|
/* Clip directions that are extremely close to grazing (for numerical
|
||||||
* stability) or entirely below the horizon. This effectively extends
|
* stability) or entirely below the horizon. This effectively extends
|
||||||
* the horizon luminance value to the bottom hemisphere */
|
* the horizon luminance value to the bottom hemisphere */
|
||||||
|
@ -557,7 +603,7 @@ private:
|
||||||
|
|
||||||
/* Create spectrum from XYZ values */
|
/* Create spectrum from XYZ values */
|
||||||
Spectrum dstSpect;
|
Spectrum dstSpect;
|
||||||
dstSpect.fromXYZ(X, Y, Z);
|
dstSpect.fromXYZ(X, Y, Z, Spectrum::EIlluminant);
|
||||||
/* The produced spectrum might contain out-of-gamut colors.
|
/* The produced spectrum might contain out-of-gamut colors.
|
||||||
* The common solution is to clamp resulting values to zero. */
|
* The common solution is to clamp resulting values to zero. */
|
||||||
dstSpect.clampNegative();
|
dstSpect.clampNegative();
|
||||||
|
@ -567,8 +613,6 @@ private:
|
||||||
MTS_DECLARE_CLASS()
|
MTS_DECLARE_CLASS()
|
||||||
protected:
|
protected:
|
||||||
Spectrum m_average;
|
Spectrum m_average;
|
||||||
Float m_surfaceArea;
|
|
||||||
Float m_invSurfaceArea;
|
|
||||||
BSphere m_bsphere;
|
BSphere m_bsphere;
|
||||||
int m_resolution;
|
int m_resolution;
|
||||||
Float m_scale;
|
Float m_scale;
|
||||||
|
|
|
@ -66,7 +66,7 @@ UpgradeManager::UpgradeManager(const FileResolver *resolver) : m_resolver(resolv
|
||||||
fs::path file = *it;
|
fs::path file = *it;
|
||||||
std::string extension = file.extension().string();
|
std::string extension = file.extension().string();
|
||||||
if (boost::to_lower_copy(extension) != ".xsl" ||
|
if (boost::to_lower_copy(extension) != ".xsl" ||
|
||||||
!boost::starts_with(extension, "upgrade_"))
|
!boost::starts_with(file.filename().string(), "upgrade_"))
|
||||||
continue;
|
continue;
|
||||||
std::string filename = file.filename().string();
|
std::string filename = file.filename().string();
|
||||||
Version version(filename.substr(8, filename.length()-12));
|
Version version(filename.substr(8, filename.length()-12));
|
||||||
|
|
|
@ -31,13 +31,14 @@ class Tonemap : public Utility {
|
||||||
public:
|
public:
|
||||||
void help() {
|
void help() {
|
||||||
cout << endl;
|
cout << endl;
|
||||||
cout << "Synopsis: Loads in one or more linear EXR images and writes out tonemapped 8-bit PNGs";
|
cout << "Synopsis: Loads one or more linear EXR images and writes tonemapped 8-bit PNG/JPGs";
|
||||||
cout << endl;
|
cout << endl;
|
||||||
cout << "Usage: mtsutil tonemap [options] <EXR file (s)>" << endl;
|
cout << "Usage: mtsutil tonemap [options] <EXR file (s)>" << endl;
|
||||||
cout << "Options/Arguments:" << endl;
|
cout << "Options/Arguments:" << endl;
|
||||||
cout << " -h Display this help text" << endl << endl;
|
cout << " -h Display this help text" << endl << endl;
|
||||||
cout << " -g gamma Specify the gamma value (The default is -1 => sRGB)" << endl << endl;
|
cout << " -g gamma Specify the gamma value (The default is -1 => sRGB)" << endl << endl;
|
||||||
cout << " -m multiplier Multiply the pixel values by 'multiplier' (Default = 1)" << endl << endl;
|
cout << " -m multiplier Multiply the pixel values by 'multiplier' (Default = 1)" << endl << endl;
|
||||||
|
cout << " -f fmt Specifies the output format (png/jpg, default:png)" << endl << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline float toSRGB(float value) {
|
inline float toSRGB(float value) {
|
||||||
|
@ -52,9 +53,10 @@ public:
|
||||||
char optchar, *end_ptr = NULL;
|
char optchar, *end_ptr = NULL;
|
||||||
optind = 1;
|
optind = 1;
|
||||||
Float gamma = -1, multiplier = 1;
|
Float gamma = -1, multiplier = 1;
|
||||||
|
Bitmap::EFileFormat format = Bitmap::EPNG;
|
||||||
|
|
||||||
/* Parse command-line arguments */
|
/* Parse command-line arguments */
|
||||||
while ((optchar = getopt(argc, argv, "hg:m:")) != -1) {
|
while ((optchar = getopt(argc, argv, "hg:m:f:")) != -1) {
|
||||||
switch (optchar) {
|
switch (optchar) {
|
||||||
case 'h': {
|
case 'h': {
|
||||||
help();
|
help();
|
||||||
|
@ -66,6 +68,16 @@ public:
|
||||||
if (*end_ptr != '\0')
|
if (*end_ptr != '\0')
|
||||||
SLog(EError, "Could not parse the gamma value!");
|
SLog(EError, "Could not parse the gamma value!");
|
||||||
break;
|
break;
|
||||||
|
case 'f': {
|
||||||
|
std::string fmt = optarg;
|
||||||
|
if (fmt == "png")
|
||||||
|
format = Bitmap::EPNG;
|
||||||
|
else if (fmt == "jpg" || fmt == "jpeg")
|
||||||
|
format = Bitmap::EJPEG;
|
||||||
|
else
|
||||||
|
SLog(EError, "Unknown format! (must be png/jpg)");
|
||||||
|
}
|
||||||
|
break;
|
||||||
case 'm':
|
case 'm':
|
||||||
multiplier = (Float) strtod(optarg, &end_ptr);
|
multiplier = (Float) strtod(optarg, &end_ptr);
|
||||||
if (*end_ptr != '\0')
|
if (*end_ptr != '\0')
|
||||||
|
@ -83,8 +95,6 @@ public:
|
||||||
|
|
||||||
for (int i=optind; i<argc; ++i) {
|
for (int i=optind; i<argc; ++i) {
|
||||||
fs::path inputFile = fileResolver->resolve(argv[i]);
|
fs::path inputFile = fileResolver->resolve(argv[i]);
|
||||||
fs::path outputFile = inputFile;
|
|
||||||
outputFile.replace_extension(".png");
|
|
||||||
Log(EInfo, "Loading EXR image \"%s\" ..", inputFile.string().c_str());
|
Log(EInfo, "Loading EXR image \"%s\" ..", inputFile.string().c_str());
|
||||||
ref<FileStream> is = new FileStream(inputFile, FileStream::EReadOnly);
|
ref<FileStream> is = new FileStream(inputFile, FileStream::EReadOnly);
|
||||||
ref<Bitmap> input = new Bitmap(Bitmap::EEXR, is);
|
ref<Bitmap> input = new Bitmap(Bitmap::EEXR, is);
|
||||||
|
@ -104,9 +114,20 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Log(EInfo, "Writing tonemapped PNG image \"%s\" ..", outputFile.string().c_str());
|
fs::path outputFile = inputFile;
|
||||||
|
if (format == Bitmap::EPNG) {
|
||||||
|
outputFile.replace_extension(".png");
|
||||||
|
Log(EInfo, "Writing tonemapped PNG image \"%s\" ..", outputFile.string().c_str());
|
||||||
|
ref<FileStream> os = new FileStream(outputFile, FileStream::ETruncReadWrite);
|
||||||
|
} else if (format == Bitmap::EJPEG) {
|
||||||
|
outputFile.replace_extension(".jpg");
|
||||||
|
Log(EInfo, "Writing tonemapped JPEG image \"%s\" ..", outputFile.string().c_str());
|
||||||
|
} else {
|
||||||
|
Log(EError, "Unknown format!");
|
||||||
|
}
|
||||||
|
|
||||||
ref<FileStream> os = new FileStream(outputFile, FileStream::ETruncReadWrite);
|
ref<FileStream> os = new FileStream(outputFile, FileStream::ETruncReadWrite);
|
||||||
output->save(Bitmap::EPNG, os);
|
output->save(format, os);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|