diff --git a/include/mitsuba/core/spectrum.h b/include/mitsuba/core/spectrum.h index eb02ba45..9d1095d3 100644 --- a/include/mitsuba/core/spectrum.h +++ b/include/mitsuba/core/spectrum.h @@ -493,6 +493,14 @@ public: return value; } + /// Component-wise square root + inline TSpectrum safe_sqrt() const { + TSpectrum value; + for (int i=0; i #include #include +#include "ior.h" MTS_NAMESPACE_BEGIN @@ -29,11 +30,12 @@ MTS_NAMESPACE_BEGIN * \parameters{ * \parameter{material}{\String}{Name of a material preset, see * \tblref{conductor-iors}.\!\default{\texttt{Cu} / copper}} - * \parameter{eta}{\Spectrum}{Real part of the material's index - * of refraction \default{based on the value of \texttt{material}}} - * \parameter{k}{\Spectrum}{Imaginary part of the material's index of - * refraction, also known as absorption coefficient. - * \default{based on the value of \texttt{material}}} + * \parameter{eta, k}{\Spectrum}{Real and imaginary components of the material's index of + * refraction \default{based on the value of \texttt{material}}} + * \parameter{extEta}{\Float\Or\String}{ + * Real-valued index of refraction of the surrounding dielectric, + * or a material name of a dielectric \default{\code{air}} + * } * \parameter{specular\showbreak Reflectance}{\Spectrum\Or\Texture}{Optional * factor that can be used to modulate the specular reflection component. Note * that for physical realism, this parameter should never be touched. \default{1.0}} @@ -154,21 +156,23 @@ public: m_specularReflectance = new ConstantSpectrumTexture( props.getSpectrum("specularReflectance", Spectrum(1.0f))); - std::string material = props.getString("material", "Cu"); + std::string materialName = props.getString("material", "Cu"); - Spectrum materialEta, materialK; - if (boost::to_lower_copy(material) == "none") { - materialEta = Spectrum(0.0f); - materialK = Spectrum(1.0f); + Spectrum intEta, intK; + if (boost::to_lower_copy(materialName) == "none") { + intEta = Spectrum(0.0f); + intK = Spectrum(1.0f); } else { - materialEta.fromContinuousSpectrum(InterpolatedSpectrum( - fResolver->resolve("data/ior/" + material + ".eta.spd"))); - materialK.fromContinuousSpectrum(InterpolatedSpectrum( - fResolver->resolve("data/ior/" + material + ".k.spd"))); + intEta.fromContinuousSpectrum(InterpolatedSpectrum( + fResolver->resolve("data/ior/" + materialName + ".eta.spd"))); + intK.fromContinuousSpectrum(InterpolatedSpectrum( + fResolver->resolve("data/ior/" + materialName + ".k.spd"))); } - m_eta = props.getSpectrum("eta", materialEta); - m_k = props.getSpectrum("k", materialK); + Float extEta = lookupIOR(props, "extEta", "air"); + + m_eta = props.getSpectrum("eta", intEta) / extEta; + m_k = props.getSpectrum("k", intK) / extEta; } SmoothConductor(Stream *stream, InstanceManager *manager) @@ -229,7 +233,7 @@ public: return Spectrum(0.0f); return m_specularReflectance->eval(bRec.its) * - fresnelConductor(Frame::cosTheta(bRec.wi), m_eta, m_k); + fresnelConductorExact(Frame::cosTheta(bRec.wi), m_eta, m_k); } Float pdf(const BSDFSamplingRecord &bRec, EMeasure measure) const { @@ -260,7 +264,7 @@ public: bRec.eta = 1.0f; return m_specularReflectance->eval(bRec.its) * - fresnelConductor(Frame::cosTheta(bRec.wi), m_eta, m_k); + fresnelConductorExact(Frame::cosTheta(bRec.wi), m_eta, m_k); } Spectrum sample(BSDFSamplingRecord &bRec, Float &pdf, const Point2 &sample) const { @@ -277,7 +281,7 @@ public: pdf = 1; return m_specularReflectance->eval(bRec.its) * - fresnelConductor(Frame::cosTheta(bRec.wi), m_eta, m_k); + fresnelConductorExact(Frame::cosTheta(bRec.wi), m_eta, m_k); } Float getRoughness(const Intersection &its, int component) const { @@ -320,7 +324,7 @@ public: m_specularReflectanceShader = renderer->registerShaderForResource(m_specularReflectance.get()); /* Compute the reflectance at perpendicular incidence */ - m_R0 = fresnelConductor(1.0f, eta, k); + m_R0 = fresnelConductorExact(1.0f, eta, k); m_alpha = 0.4f; } diff --git a/src/bsdfs/roughconductor.cpp b/src/bsdfs/roughconductor.cpp index f25b5de8..ca70bb2e 100644 --- a/src/bsdfs/roughconductor.cpp +++ b/src/bsdfs/roughconductor.cpp @@ -20,6 +20,7 @@ #include #include #include "microfacet.h" +#include "ior.h" MTS_NAMESPACE_BEGIN @@ -32,15 +33,15 @@ MTS_NAMESPACE_BEGIN * used to model the surface roughness. * \begin{enumerate}[(i)] * \item \code{beckmann}: Physically-based distribution derived from - * Gaussian random surfaces. This is the default. + * Gaussian random surfaces. This is the default.\vspace{-1mm} * \item \code{ggx}: New distribution proposed by * Walter et al. \cite{Walter07Microfacet}, which is meant to better handle * the long tails observed in measurements of ground surfaces. - * Renderings with this distribution may converge slowly. + * Renderings with this distribution may converge slowly.\vspace{-1mm} * \item \code{phong}: Classical $\cos^p\theta$ distribution. * Due to the underlying microfacet theory, * the use of this distribution here leads to more realistic - * behavior than the separately available \pluginref{phong} plugin. + * behavior than the separately available \pluginref{phong} plugin.\vspace{-1mm} * \item \code{as}: Anisotropic Phong-style microfacet distribution proposed by * Ashikhmin and Shirley \cite{Ashikhmin2005Anisotropic}.\vspace{-3mm} * \end{enumerate} @@ -59,11 +60,12 @@ MTS_NAMESPACE_BEGIN * } * \parameter{material}{\String}{Name of a material preset, see * \tblref{conductor-iors}.\!\default{\texttt{Cu} / copper}} - * \parameter{eta}{\Spectrum}{Real part of the material's index - * of refraction \default{based on the value of \texttt{material}}} - * \parameter{k}{\Spectrum}{Imaginary part of the material's index of - * refraction (the absorption coefficient). - * \default{based on \texttt{material}}} + * \parameter{eta, k}{\Spectrum}{Real and imaginary components of the material's index of + * refraction \default{based on the value of \texttt{material}}} + * \parameter{extEta}{\Float\Or\String}{ + * Real-valued index of refraction of the surrounding dielectric, + * or a material name of a dielectric \default{\code{air}} + * } * \parameter{specular\showbreak Reflectance}{\Spectrum\Or\Texture}{Optional * factor that can be used to modulate the specular reflection component. Note * that for physical realism, this parameter should never be touched. \default{1.0}} @@ -158,21 +160,23 @@ public: m_specularReflectance = new ConstantSpectrumTexture( props.getSpectrum("specularReflectance", Spectrum(1.0f))); - std::string material = props.getString("material", "Cu"); - Spectrum materialEta, materialK; + std::string materialName = props.getString("material", "Cu"); - if (boost::to_lower_copy(material) == "none") { - materialEta = Spectrum(0.0f); - materialK = Spectrum(1.0f); + Spectrum intEta, intK; + if (boost::to_lower_copy(materialName) == "none") { + intEta = Spectrum(0.0f); + intK = Spectrum(1.0f); } else { - materialEta.fromContinuousSpectrum(InterpolatedSpectrum( - fResolver->resolve("data/ior/" + material + ".eta.spd"))); - materialK.fromContinuousSpectrum(InterpolatedSpectrum( - fResolver->resolve("data/ior/" + material + ".k.spd"))); + intEta.fromContinuousSpectrum(InterpolatedSpectrum( + fResolver->resolve("data/ior/" + materialName + ".eta.spd"))); + intK.fromContinuousSpectrum(InterpolatedSpectrum( + fResolver->resolve("data/ior/" + materialName + ".k.spd"))); } - m_eta = props.getSpectrum("eta", materialEta); - m_k = props.getSpectrum("k", materialK); + Float extEta = lookupIOR(props, "extEta", "air"); + + m_eta = props.getSpectrum("eta", intEta) / extEta; + m_k = props.getSpectrum("k", intK) / extEta; m_distribution = MicrofacetDistribution( props.getString("distribution", "beckmann") @@ -262,7 +266,7 @@ public: return Spectrum(0.0f); /* Fresnel factor */ - const Spectrum F = fresnelConductor(dot(bRec.wi, H), m_eta, m_k); + const Spectrum F = fresnelConductorExact(dot(bRec.wi, H), m_eta, m_k); /* Smith's shadow-masking function */ const Float G = m_distribution.G(bRec.wi, bRec.wo, H, alphaU, alphaV); @@ -324,7 +328,7 @@ public: if (Frame::cosTheta(bRec.wo) <= 0) return Spectrum(0.0f); - const Spectrum F = fresnelConductor(dot(bRec.wi, m), + const Spectrum F = fresnelConductorExact(dot(bRec.wi, m), m_eta, m_k); Float numerator = m_distribution.eval(m, alphaU, alphaV) @@ -367,7 +371,7 @@ public: if (Frame::cosTheta(bRec.wo) <= 0) return Spectrum(0.0f); - const Spectrum F = fresnelConductor(dot(bRec.wi, m), + const Spectrum F = fresnelConductorExact(dot(bRec.wi, m), m_eta, m_k); Float numerator = m_distribution.eval(m, alphaU, alphaV) @@ -460,7 +464,7 @@ public: m_alphaVShader = renderer->registerShaderForResource(m_alphaV.get()); /* Compute the reflectance at perpendicular incidence */ - m_R0 = fresnelConductor(1.0f, eta, k); + m_R0 = fresnelConductorExact(1.0f, eta, k); } bool isComplete() const { diff --git a/src/libcore/util.cpp b/src/libcore/util.cpp index d24e6063..6ecbff78 100644 --- a/src/libcore/util.cpp +++ b/src/libcore/util.cpp @@ -586,18 +586,84 @@ Float fresnelDielectricExt(Float cosThetaI_, Float &cosThetaT_, Float eta) { return 0.5f * (Rs * Rs + Rp * Rp); } -Spectrum fresnelConductor(Float cosThetaI, const Spectrum &eta, const Spectrum &k) { - Spectrum tmp = (eta*eta + k*k) * (cosThetaI * cosThetaI); +Float fresnelConductorApprox(Float cosThetaI, Float eta, Float k) { + Float cosThetaI2 = cosThetaI*cosThetaI; - Spectrum rParl2 = (tmp - (eta * (2.0f * cosThetaI)) + Spectrum(1.0f)) - / (tmp + (eta * (2.0f * cosThetaI)) + Spectrum(1.0f)); + Float tmp = (eta*eta + k*k) * cosThetaI2; + + Float Rp2 = (tmp - (eta * (2 * cosThetaI)) + 1) + / (tmp + (eta * (2 * cosThetaI)) + 1); + + Float tmpF = eta*eta + k*k; + + Float Rs2 = (tmpF - (eta * (2 * cosThetaI)) + cosThetaI2) / + (tmpF + (eta * (2 * cosThetaI)) + cosThetaI2); + + return 0.5f * (Rp2 + Rs2); +} + +Spectrum fresnelConductorApprox(Float cosThetaI, const Spectrum &eta, const Spectrum &k) { + Float cosThetaI2 = cosThetaI*cosThetaI; + + Spectrum tmp = (eta*eta + k*k) * cosThetaI2; + + Spectrum Rp2 = (tmp - (eta * (2 * cosThetaI)) + Spectrum(1.0f)) + / (tmp + (eta * (2 * cosThetaI)) + Spectrum(1.0f)); Spectrum tmpF = eta*eta + k*k; - Spectrum rPerp2 = (tmpF - (eta * (2.0f * cosThetaI)) + Spectrum(cosThetaI*cosThetaI)) / - (tmpF + (eta * (2.0f * cosThetaI)) + Spectrum(cosThetaI*cosThetaI)); + Spectrum Rs2 = (tmpF - (eta * (2 * cosThetaI)) + Spectrum(cosThetaI2)) / + (tmpF + (eta * (2 * cosThetaI)) + Spectrum(cosThetaI2)); - return (rParl2 + rPerp2) / 2.0f; + return 0.5f * (Rp2 + Rs2); +} + +Float fresnelConductorExact(Float cosThetaI, Float eta, Float k) { + /* Modified from "Optics" by K.D. Moeller, University Science Books, 1988 */ + + Float cosThetaI2 = cosThetaI*cosThetaI, + sinThetaI2 = 1-cosThetaI2, + sinThetaI4 = sinThetaI2*sinThetaI2; + + Float temp1 = eta*eta - k*k - sinThetaI2, + a2pb2 = math::safe_sqrt(temp1*temp1 + 4*k*k*eta*eta), + a = math::safe_sqrt(0.5f * (a2pb2 + temp1)); + + Float term1 = a2pb2 + cosThetaI2, + term2 = 2*a*cosThetaI; + + Float Rs2 = (term1 - term2) / (term1 + term2); + + Float term3 = a2pb2*cosThetaI2 + sinThetaI4, + term4 = term2*sinThetaI2; + + Float Rp2 = Rs2 * (term3 - term4) / (term3 + term4); + + return 0.5f * (Rp2 + Rs2); +} + +Spectrum fresnelConductorExact(Float cosThetaI, const Spectrum &eta, const Spectrum &k) { + /* Modified from "Optics" by K.D. Moeller, University Science Books, 1988 */ + + Float cosThetaI2 = cosThetaI*cosThetaI, + sinThetaI2 = 1-cosThetaI2, + sinThetaI4 = sinThetaI2*sinThetaI2; + + Spectrum temp1 = eta*eta - k*k - Spectrum(sinThetaI2), + a2pb2 = (temp1*temp1 + k*k*eta*eta*4).safe_sqrt(), + a = ((a2pb2 + temp1) * 0.5f).safe_sqrt(); + + Spectrum term1 = a2pb2 + Spectrum(cosThetaI2), + term2 = a*(2*cosThetaI); + + Spectrum Rs2 = (term1 - term2) / (term1 + term2); + + Spectrum term3 = a2pb2*cosThetaI2 + Spectrum(sinThetaI4), + term4 = term2*sinThetaI2; + + Spectrum Rp2 = Rs2 * (term3 - term4) / (term3 + term4); + + return 0.5f * (Rp2 + Rs2); } Vector reflect(const Vector &wi, const Normal &n) { diff --git a/src/libpython/core.cpp b/src/libpython/core.cpp index d7f85859..ab13f2a4 100644 --- a/src/libpython/core.cpp +++ b/src/libpython/core.cpp @@ -79,10 +79,10 @@ void initializeFramework() { if (GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCSTR) &initializeFramework, &hm)) { std::vector lpFilename(MAX_PATH); - + // Try to get the path with the default MAX_PATH length (260 chars) DWORD nSize = GetModuleFileNameW(hm, &lpFilename[0], MAX_PATH); - + // Adjust the buffer size in case if was too short while (nSize == lpFilename.size()) { lpFilename.resize(nSize * 2); @@ -1261,11 +1261,19 @@ void export_core() { .staticmethod("glOrthographic") .staticmethod("fromFrame"); + Float (*fresnelConductorApprox1)(Float, Float, Float) = &fresnelConductorApprox; + Float (*fresnelConductorExact1)(Float, Float, Float) = &fresnelConductorExact; + Spectrum (*fresnelConductorApprox2)(Float, const Spectrum &, const Spectrum &) = &fresnelConductorApprox; + Spectrum (*fresnelConductorExact2)(Float, const Spectrum &, const Spectrum &) = &fresnelConductorExact; + /* Functions from utility.h */ bp::def("fresnelDielectric", &fresnelDielectric); bp::def("fresnelDielectricExt", &fresnelDielectricExt1); bp::def("fresnelDielectricExt", &fresnelDielectricExt2); - bp::def("fresnelConductor", &fresnelConductor, BP_RETURN_VALUE); + bp::def("fresnelConductorApprox", fresnelConductorApprox1, BP_RETURN_VALUE); + bp::def("fresnelConductorApprox", fresnelConductorApprox2, BP_RETURN_VALUE); + bp::def("fresnelConductorExact", fresnelConductorExact1, BP_RETURN_VALUE); + bp::def("fresnelConductorExact", fresnelConductorExact2, BP_RETURN_VALUE); bp::def("fresnelDiffuseReflectance", &fresnelDiffuseReflectance); bp::def("reflect", &reflect); bp::def("refract", &refract1); diff --git a/src/mtsgui/mainwindow.cpp b/src/mtsgui/mainwindow.cpp index 47787b01..40feb58d 100644 --- a/src/mtsgui/mainwindow.cpp +++ b/src/mtsgui/mainwindow.cpp @@ -1478,9 +1478,6 @@ void MainWindow::on_actionExportImage_triggered() { } void MainWindow::onExportDialogClose(int reason) { - int currentIndex = ui->tabBar->currentIndex(); - SceneContext *ctx = m_context[currentIndex]; - QSettings settings; QFileDialog *dialog = static_cast(sender()); m_currentChild = NULL;