improved bump and normal mapping support

metadata
Wenzel Jakob 2013-11-15 16:47:35 +01:00
parent 38fced0f53
commit b68a38ed9a
20 changed files with 506 additions and 81 deletions

View File

@ -48,7 +48,7 @@
<xsl:apply-templates select="@*|node()"/>
<!-- The vertical component of OBJ texture coordinates is now
flipped, which seems to be the standard behavior. Undo
flipped, which seems to be the standard behavior. Undo
this change for consistency in old scenes. -->
<xsl:if test="@type='obj'">
<boolean name="flipTexCoords" value="false"/>
@ -66,7 +66,7 @@
<xsl:template match="bsdf[@type='microfacet' or @type='phong' or @type='ward']">
<xsl:variable name="diffuseAmount">
<xsl:choose>
<xsl:when test="float[@name='diffuseAmount']">
<xsl:when test="float[@name='diffuseAmount']">
<xsl:value-of select="float[@name='diffuseAmount']/@value"/>
</xsl:when>
<xsl:otherwise>1.0</xsl:otherwise>
@ -74,7 +74,7 @@
</xsl:variable>
<xsl:variable name="specularAmount">
<xsl:choose>
<xsl:when test="float[@name='specularAmount']">
<xsl:when test="float[@name='specularAmount']">
<xsl:value-of select="float[@name='specularAmount']/@value"/>
</xsl:when>
<xsl:otherwise>1.0</xsl:otherwise>
@ -211,7 +211,7 @@
<xsl:template match="float[@name='sizeMultiplier']/@name">
<xsl:attribute name="name">densityMultiplier</xsl:attribute>
</xsl:template>
<!-- There is no more 'mirror' plugin; replace with smooth chrome -->
<xsl:template match="bsdf[@type='mirror']/@type">
<xsl:attribute name="type">conductor</xsl:attribute>
@ -246,7 +246,7 @@
<xsl:template match="bsdf[@type='composite']/@type">
<xsl:attribute name="type">mixturebsdf</xsl:attribute>
</xsl:template>
<!-- Update the name of the exrtexture plugin -->
<xsl:template match="texture[@type='exrtexture']/@type">
<xsl:attribute name="type">bitmap</xsl:attribute>

View File

@ -91,7 +91,7 @@
<xsl:template match="film[@type='pngfilm']/@type">
<xsl:attribute name="type">ldrfilm</xsl:attribute>
</xsl:template>
<!-- Update the 'focusDepth' attribute name -->
<xsl:template match="float[@name='focusDepth']/@name">
<xsl:attribute name="name">focusDistance</xsl:attribute>

View File

@ -0,0 +1,25 @@
<?xml version='1.0' encoding='utf-8'?>
<!-- Stylesheet to upgrade from Mitsuba version 0.4.x to 0.5.0 scenes -->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" indent="yes" encoding="utf-8"/>
<xsl:preserve-space elements="*"/>
<!-- Update the scene version -->
<xsl:template match="scene/@version">
<xsl:attribute name="version">0.5.0</xsl:attribute>
</xsl:template>
<!-- Update the name of the bump plugin -->
<xsl:template match="integrator[@type='bump']/@type">
<xsl:attribute name="type">bumpmap</xsl:attribute>
</xsl:template>
<!-- Default copy rule -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

View File

Before

Width:  |  Height:  |  Size: 172 KiB

After

Width:  |  Height:  |  Size: 172 KiB

View File

Before

Width:  |  Height:  |  Size: 193 KiB

After

Width:  |  Height:  |  Size: 193 KiB

View File

@ -453,6 +453,15 @@ public:
*/
virtual Float getRoughness(const Intersection &its, int index) const;
/**
* \brief Sometimes, BSDF models make use of a perturbed frame for
* internal shading computations (e.g. bump maps). This function
* exposes this internal frame.
*
* By default, it returns <tt>its.shFrame</tt>
*/
virtual Frame getFrame(const Intersection &its) const;
// =============================================================
//! @{ \name ConfigurableObject interface
// =============================================================

View File

@ -52,12 +52,14 @@ namespace stats {
/// Specifies the desired antialiasing filter
enum EMIPFilterType {
/// No filtering (i.e. nearest neighbor lookups)
/// No filtering, nearest neighbor lookups
ENearest = 0,
/// No filtering, only bilinear interpolation
EBilinear = 1,
/// Basic trilinear filtering
ETrilinear = 1,
ETrilinear = 2,
/// Elliptically weighted average
EEWA = 2,
EEWA = 3
};
/**
@ -179,7 +181,7 @@ public:
/* 1. Determine the number of MIP levels. The following
code also handles non-power-of-2 input. */
m_levels = 1;
if (m_filterType != ENearest) {
if (m_filterType != ENearest && m_filterType != EBilinear) {
Vector2i size = bitmap_->getSize();
while (size.x > 1 || size.y > 1) {
size.x = std::max(1, (size.x + 1) / 2);
@ -233,7 +235,7 @@ public:
m_sizeRatio[0] = Vector2(1, 1);
/* 3. Progressively downsample until only a 1x1 image is left */
if (m_filterType != ENearest) {
if (m_filterType != ENearest && m_filterType != EBilinear) {
Vector2i size = bitmap_->getSize();
m_levels = 1;
while (size.x > 1 || size.y > 1) {
@ -344,7 +346,7 @@ public:
mmapPtr += m_pyramid[0].getBufferSize();
m_sizeRatio[0] = Vector2(1, 1);
if (m_filterType != ENearest) {
if (m_filterType != ENearest && m_filterType != EBilinear) {
/* Map the remainder of the image pyramid */
int level = 1;
while (size.x > 1 || size.y > 1) {
@ -425,7 +427,7 @@ public:
size_t expectedFileSize = sizeof(MIPMapHeader) + padding
+ Array2DType::bufferSize(size);
if (filterType != ENearest) {
if (filterType != ENearest && filterType != EBilinear) {
while (size.x > 1 || size.y > 1) {
size.x = std::max(1, (size.x + 1) / 2);
size.y = std::max(1, (size.y + 1) / 2);
@ -586,10 +588,42 @@ public:
+ evalTexel(level, xPos + 1, yPos + 1) * dx1 * dy1;
}
/**
* \brief Evaluate the gradient of the texture at the given MIP level
*/
inline void evalGradientBilinear(int level, const Point2 &uv, Value *gradient) const {
if (EXPECT_NOT_TAKEN(!std::isfinite(uv.x) || !std::isfinite(uv.y))) {
Log(EWarn, "evalGradientBilinear(): encountered a NaN!");
gradient[0] = gradient[1] = Value(0.0f);
return;
} else if (EXPECT_NOT_TAKEN(level >= m_levels)) {
evalGradientBilinear(m_levels-1, uv, gradient);
return;
}
/* Convert to fractional pixel coordinates on the specified level */
const Vector2i &size = m_pyramid[level].getSize();
Float u = uv.x * size.x - 0.5f, v = uv.y * size.y - 0.5f;
int xPos = floorToInt(u), yPos = floorToInt(v);
Float dx = u - xPos, dy = v - yPos;
const Value p00 = evalTexel(level, xPos, yPos);
const Value p10 = evalTexel(level, xPos+1, yPos);
const Value p01 = evalTexel(level, xPos, yPos+1);
const Value p11 = evalTexel(level, xPos+1, yPos+1);
Value tmp = p01 + p10 - p11;
gradient[0] = (p10 + p00*(dy-1) - tmp*dy) * size.x;
gradient[1] = (p01 + p00*(dx-1) - tmp*dx) * size.y;
}
/// \brief Perform a filtered texture lookup using the configured method
Value eval(const Point2 &uv, const Vector2 &d0, const Vector2 &d1) const {
if (m_filterType == ENearest)
return evalBox(0, uv);
else if (m_filterType == EBilinear)
return evalBilinear(0, uv);
/* Convert into texel coordinates */
const Vector2i &size = m_pyramid[0].getSize();
@ -685,6 +719,7 @@ public:
switch (m_filterType) {
case ENearest: oss << "nearest," << endl; break;
case EBilinear: oss << "bilinear," << endl; break;
case ETrilinear: oss << "trilinear," << endl; break;
case EEWA: oss << "ewa," << endl; break;
}

View File

@ -41,6 +41,16 @@ public:
*/
virtual Spectrum eval(const Intersection &its, bool filter = true) const;
/**
* \brief Return the texture gradient at \c its
*
* The parameter \c gradient should point to an array with space for
* two \ref Spectrum data structures corresponding to the U and V derivative.
*
* \remark This function is usually implemented pointwise without any kind of filtering.
*/
virtual void evalGradient(const Intersection &its, Spectrum *gradient) const;
/// Return the component-wise average value of the texture over its domain
virtual Spectrum getAverage() const;
@ -108,6 +118,16 @@ public:
*/
Spectrum eval(const Intersection &its, bool filter = true) const;
/**
* \brief Return the texture gradient at \c its
*
* The parameter \c gradient should point to an array with space for
* two \ref Spectrum data structures corresponding to the U and V derivative.
*
* \remark This function is usually implemented pointwise without any kind of filtering.
*/
void evalGradient(const Intersection &its, Spectrum *gradient) const;
/// Serialize to a binary data stream
virtual void serialize(Stream *stream, InstanceManager *manager) const;
@ -118,6 +138,9 @@ public:
virtual Spectrum eval(const Point2 &uv, const Vector2 &d0,
const Vector2 &d1) const = 0;
/// Unfiltered radient lookup lookup -- Texture2D subclasses can optionally provide this function
virtual void evalGradient(const Point2 &uv, Spectrum *gradient) const;
/**
* \brief Return a bitmap representation of the texture
*

View File

@ -18,7 +18,8 @@ add_bsdf(roughconductor roughconductor.cpp microfacet.h)
add_bsdf(roughplastic roughplastic.cpp microfacet.h ior.h)
# Materials that act as modifiers
add_bsdf(bump bump.cpp)
add_bsdf(bumpmap bumpmap.cpp)
add_bsdf(normalmap normalmap.cpp)
add_bsdf(twosided twosided.cpp)
add_bsdf(mask mask.cpp)
add_bsdf(mixturebsdf mixturebsdf.cpp)

View File

@ -17,7 +17,8 @@ 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'])
plugins += env.SharedLibrary('bumpmap', ['bumpmap.cpp'])
plugins += env.SharedLibrary('normalmap', ['normalmap.cpp'])
# Other materials
plugins += env.SharedLibrary('ward', ['ward.cpp'])

View File

@ -21,9 +21,9 @@
MTS_NAMESPACE_BEGIN
/*! \plugin{bump}{Bump map modifier}
/*! \plugin{bumpmap}{Bump map modifier}
* \order{12}
* \icon{bsdf_bump}
* \icon{bsdf_bumpmap}
*
* \parameters{
* \parameter{\Unnamed}{\Texture}{
@ -35,8 +35,8 @@ MTS_NAMESPACE_BEGIN
* be affected by the bump map}
* }
* \renderings{
* \rendering{Bump map based on tileable diagonal lines}{bsdf_bump_1}
* \rendering{An irregular bump map}{bsdf_bump_2}
* \rendering{Bump map based on tileable diagonal lines}{bsdf_bumpmap_1}
* \rendering{An irregular bump map}{bsdf_bumpmap_2}
* }
*
* Bump mapping \cite{Blinn1978Simulation} is a simple technique for cheaply
@ -57,7 +57,7 @@ MTS_NAMESPACE_BEGIN
* texture plugin can be used to magnify or reduce the effect of a
* bump map texture.
* \begin{xml}[caption=A rough metal model with a scaled image-based bump map]
* <bsdf type="bump">
* <bsdf type="bumpmap">
* <!-- The bump map is applied to a rough metal BRDF -->
* <bsdf type="roughconductor"/>
*
@ -119,22 +119,12 @@ public:
}
}
void perturbIntersection(const Intersection &its, Intersection &target) const {
const Float eps = Epsilon;
Frame getFrame(const Intersection &its) const {
Spectrum grad[2];
m_displacement->evalGradient(its, grad);
/* Compute the U and V displacementment derivatives */
target = its;
Float disp = m_displacement->eval(target, false).getLuminance();
target.p = its.p + its.dpdu * eps;
target.uv = its.uv + Point2(eps, 0);
Float dispU = m_displacement->eval(target, false).getLuminance();
target.p = its.p + its.dpdv * eps;
target.uv = its.uv + Point2(0, eps);
Float dispV = m_displacement->eval(target, false).getLuminance();
target.p = its.p;
target.uv = its.uv;
Float dDispDu = (dispU - disp) / eps;
Float dDispDv = (dispV - disp) / eps;
Float dDispDu = grad[0].getLuminance();
Float dDispDv = grad[1].getLuminance();
/* Build a perturbed frame -- ignores the usually
negligible normal derivative term */
@ -143,21 +133,22 @@ public:
Vector dpdv = its.dpdv + its.shFrame.n * (
dDispDv - dot(its.shFrame.n, its.dpdv));
dpdu = normalize(dpdu);
dpdv = normalize(dpdv - dpdu * dot(dpdv, dpdu));
Frame result;
result.n = normalize(cross(dpdu, dpdv));
result.s = normalize(dpdu - result.n
* dot(result.n, dpdu));
result.t = cross(result.n, result.s);
target.shFrame.s = dpdu;
target.shFrame.t = dpdv;
target.shFrame = Frame(Normal(cross(dpdv, dpdu)));
if (dot(result.n, its.geoFrame.n) < 0)
result.n *= -1;
if (dot(target.shFrame.n, target.geoFrame.n) < 0)
target.shFrame.n *= -1;
return result;
}
Spectrum eval(const BSDFSamplingRecord &bRec, EMeasure measure) const {
const Intersection& its = bRec.its;
Intersection perturbed;
perturbIntersection(its, perturbed);
Intersection perturbed(its);
perturbed.shFrame = getFrame(its);
BSDFSamplingRecord perturbedQuery(perturbed,
perturbed.toLocal(its.toWorld(bRec.wi)),
@ -172,8 +163,8 @@ public:
Float pdf(const BSDFSamplingRecord &bRec, EMeasure measure) const {
const Intersection& its = bRec.its;
Intersection perturbed;
perturbIntersection(its, perturbed);
Intersection perturbed(its);
perturbed.shFrame = getFrame(its);
BSDFSamplingRecord perturbedQuery(perturbed,
perturbed.toLocal(its.toWorld(bRec.wi)),
@ -189,8 +180,8 @@ public:
Spectrum sample(BSDFSamplingRecord &bRec, const Point2 &sample) const {
const Intersection& its = bRec.its;
Intersection perturbed;
perturbIntersection(its, perturbed);
Intersection perturbed(its);
perturbed.shFrame = getFrame(its);
BSDFSamplingRecord perturbedQuery(perturbed, bRec.sampler, bRec.mode);
perturbedQuery.wi = perturbed.toLocal(its.toWorld(bRec.wi));
@ -202,6 +193,7 @@ public:
bRec.sampledComponent = perturbedQuery.sampledComponent;
bRec.sampledType = perturbedQuery.sampledType;
bRec.wo = its.toLocal(perturbed.toWorld(perturbedQuery.wo));
bRec.eta = perturbedQuery.eta;
if (Frame::cosTheta(bRec.wo) * Frame::cosTheta(perturbedQuery.wo) <= 0)
return Spectrum(0.0f);
}
@ -210,8 +202,8 @@ public:
Spectrum sample(BSDFSamplingRecord &bRec, Float &pdf, const Point2 &sample) const {
const Intersection& its = bRec.its;
Intersection perturbed;
perturbIntersection(its, perturbed);
Intersection perturbed(its);
perturbed.shFrame = getFrame(its);
BSDFSamplingRecord perturbedQuery(perturbed, bRec.sampler, bRec.mode);
perturbedQuery.wi = perturbed.toLocal(its.toWorld(bRec.wi));
@ -223,6 +215,7 @@ public:
bRec.sampledComponent = perturbedQuery.sampledComponent;
bRec.sampledType = perturbedQuery.sampledType;
bRec.wo = its.toLocal(perturbed.toWorld(perturbedQuery.wo));
bRec.eta = perturbedQuery.eta;
if (Frame::cosTheta(bRec.wo) * Frame::cosTheta(perturbedQuery.wo) <= 0)
return Spectrum(0.0f);
}
@ -285,38 +278,32 @@ public:
const std::string &evalName,
const std::vector<std::string> &depNames) const {
oss << "vec3 " << evalName << "(vec2 uv, vec3 wi, vec3 wo) {" << endl
<< " float du = abs(dFdx(uv.x)), dv = abs(dFdx(uv.y));" << endl
<< " if (du == 0.0) du = 0.001;" << endl
<< " if (dv == 0.0) dv = 0.001;" << endl
<< " float eps = 1e-4;" << endl
<< " float displacement = " << depNames[1] << "(uv)[0];" << endl
<< " float displacementU = " << depNames[1] << "(uv + vec2(du, 0.0))[0];" << endl
<< " float displacementV = " << depNames[1] << "(uv + vec2(0.0, dv))[0];" << endl
<< " float dfdu = (displacementU - displacement)/du;" << endl
<< " float dfdv = (displacementV - displacement)/dv;" << endl
<< " vec3 dpdu = normalize(vec3(1.0, 0.0, dfdu));" << endl
<< " vec3 dpdv = vec3(0.0, 1.0, dfdv);" << endl
<< " dpdv = normalize(dpdv - dot(dpdu, dpdv)*dpdu);" << endl
<< " vec3 n = cross(dpdu, dpdv);" << endl
<< " wi = vec3(dot(wi, dpdu), dot(wi, dpdv), dot(wi, n));" << endl
<< " wo = vec3(dot(wo, dpdu), dot(wo, dpdv), dot(wo, n));" << endl
<< " float displacementU = " << depNames[1] << "(uv + vec2(eps, 0.0))[0];" << endl
<< " float displacementV = " << depNames[1] << "(uv + vec2(0.0, eps))[0];" << endl
<< " float dfdu = (displacementU - displacement)*(0.5/eps);" << endl
<< " float dfdv = (displacementV - displacement)*(0.5/eps);" << endl
<< " vec3 n = normalize(vec3(-dfdu, -dfdv, 1.0));" << endl
<< " vec3 s = normalize(vec3(1.0-n.x*n.x, -n.x*n.y, -n.x*n.z)); " << endl
<< " vec3 t = cross(s, n);" << endl
<< " wi = vec3(dot(wi, s), dot(wi, t), dot(wi, n));" << endl
<< " wo = vec3(dot(wo, s), dot(wo, t), dot(wo, n));" << endl
<< " return " << depNames[0] << "(uv, wi, wo);" << endl
<< "}" << endl
<< endl
<< "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl
<< " float du = abs(dFdx(uv.x)), dv = abs(dFdx(uv.y));" << endl
<< " if (du == 0.0) du = 0.001;" << endl
<< " if (dv == 0.0) dv = 0.001;" << endl
<< " float eps = 1e-4;" << endl
<< " float displacement = " << depNames[1] << "(uv)[0];" << endl
<< " float displacementU = " << depNames[1] << "(uv + vec2(du, 0.0))[0];" << endl
<< " float displacementV = " << depNames[1] << "(uv + vec2(0.0, dv))[0];" << endl
<< " float dfdu = (displacementU - displacement)/du;" << endl
<< " float dfdv = (displacementV - displacement)/dv;" << endl
<< " vec3 dpdu = normalize(vec3(1.0, 0.0, dfdu));" << endl
<< " vec3 dpdv = vec3(0.0, 1.0, dfdv);" << endl
<< " dpdv = normalize(dpdv - dot(dpdu, dpdv)*dpdu);" << endl
<< " vec3 n = cross(dpdu, dpdv);" << endl
<< " wi = vec3(dot(wi, dpdu), dot(wi, dpdv), dot(wi, n));" << endl
<< " wo = vec3(dot(wo, dpdu), dot(wo, dpdv), dot(wo, n));" << endl
<< " float displacementU = " << depNames[1] << "(uv + vec2(eps, 0.0))[0];" << endl
<< " float displacementV = " << depNames[1] << "(uv + vec2(0.0, eps))[0];" << endl
<< " float dfdu = (displacementU - displacement)*(0.5/eps);" << endl
<< " float dfdv = (displacementV - displacement)*(0.5/eps);" << endl
<< " vec3 n = normalize(vec3(-dfdu, -dfdv, 1.0));" << endl
<< " vec3 s = normalize(vec3(1.0-n.x*n.x, -n.x*n.y, -n.x*n.z)); " << endl
<< " vec3 t = cross(s, n);" << endl
<< " wi = vec3(dot(wi, s), dot(wi, t), dot(wi, n));" << endl
<< " wo = vec3(dot(wo, s), dot(wo, t), dot(wo, n));" << endl
<< " return " << depNames[0] << "_diffuse(uv, wi, wo);" << endl
<< "}" << endl;
}
@ -335,5 +322,5 @@ Shader *BumpMap::createShader(Renderer *renderer) const {
MTS_IMPLEMENT_CLASS(BumpMapShader, false, Shader)
MTS_IMPLEMENT_CLASS_S(BumpMap, false, BSDF)
MTS_EXPORT_PLUGIN(BumpMap, "Smooth dielectric coating");
MTS_EXPORT_PLUGIN(BumpMap, "Bump map modifier");
MTS_NAMESPACE_END

259
src/bsdfs/normalmap.cpp Normal file
View File

@ -0,0 +1,259 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
#include <mitsuba/render/scene.h>
#include <mitsuba/hw/basicshader.h>
MTS_NAMESPACE_BEGIN
class NormalMap : public BSDF {
public:
NormalMap(const Properties &props) : BSDF(props) { }
NormalMap(Stream *stream, InstanceManager *manager)
: BSDF(stream, manager) {
m_nested = static_cast<BSDF *>(manager->getInstance(stream));
m_normals = static_cast<Texture *>(manager->getInstance(stream));
configure();
}
void configure() {
if (!m_nested)
Log(EError, "A child BSDF instance is required");
if (!m_normals)
Log(EError, "A normal map texture must be specified");
m_components.clear();
for (int i=0; i<m_nested->getComponentCount(); ++i)
m_components.push_back(m_nested->getType(i) | ESpatiallyVarying | EAnisotropic);
m_usesRayDifferentials = true;
BSDF::configure();
}
void serialize(Stream *stream, InstanceManager *manager) const {
BSDF::serialize(stream, manager);
manager->serialize(stream, m_nested.get());
manager->serialize(stream, m_normals.get());
}
void addChild(const std::string &name, ConfigurableObject *child) {
if (child->getClass()->derivesFrom(MTS_CLASS(BSDF))) {
if (m_nested != NULL)
Log(EError, "Only a single nested BSDF can be added!");
m_nested = static_cast<BSDF *>(child);
} else if (child->getClass()->derivesFrom(MTS_CLASS(Texture))) {
if (m_normals != NULL)
Log(EError, "Only a single normals texture can be specified!");
m_normals = static_cast<Texture *>(child);
} else {
BSDF::addChild(name, child);
}
}
Frame getFrame(const Intersection &its) const {
Normal n;
m_normals->eval(its, false).toLinearRGB(n.x, n.y, n.z);
for (int i=0; i<3; ++i)
n[i] = 2 * n[i] - 1;
Frame result;
result.n = normalize(its.shFrame.toWorld(n));
result.s = normalize(its.dpdu - result.n
* dot(result.n, its.dpdu));
result.t = cross(result.n, result.s);
if (dot(result.n, its.geoFrame.n) < 0)
result.n *= -1;
return result;
}
Spectrum eval(const BSDFSamplingRecord &bRec, EMeasure measure) const {
const Intersection& its = bRec.its;
Intersection perturbed(its);
perturbed.shFrame = getFrame(its);
BSDFSamplingRecord perturbedQuery(perturbed,
perturbed.toLocal(its.toWorld(bRec.wi)),
perturbed.toLocal(its.toWorld(bRec.wo)), bRec.mode);
if (Frame::cosTheta(bRec.wo) * Frame::cosTheta(perturbedQuery.wo) <= 0)
return Spectrum(0.0f);
perturbedQuery.sampler = bRec.sampler;
perturbedQuery.typeMask = bRec.typeMask;
perturbedQuery.component = bRec.component;
return m_nested->eval(perturbedQuery, measure);
}
Float pdf(const BSDFSamplingRecord &bRec, EMeasure measure) const {
const Intersection& its = bRec.its;
Intersection perturbed(its);
perturbed.shFrame = getFrame(its);
BSDFSamplingRecord perturbedQuery(perturbed,
perturbed.toLocal(its.toWorld(bRec.wi)),
perturbed.toLocal(its.toWorld(bRec.wo)), bRec.mode);
if (Frame::cosTheta(bRec.wo) * Frame::cosTheta(perturbedQuery.wo) <= 0)
return 0;
perturbedQuery.mode = bRec.mode;
perturbedQuery.sampler = bRec.sampler;
perturbedQuery.typeMask = bRec.typeMask;
perturbedQuery.component = bRec.component;
return m_nested->pdf(perturbedQuery, measure);
}
Spectrum sample(BSDFSamplingRecord &bRec, const Point2 &sample) const {
const Intersection& its = bRec.its;
Intersection perturbed(its);
perturbed.shFrame = getFrame(its);
BSDFSamplingRecord perturbedQuery(perturbed, bRec.sampler, bRec.mode);
perturbedQuery.wi = perturbed.toLocal(its.toWorld(bRec.wi));
perturbedQuery.sampler = bRec.sampler;
perturbedQuery.typeMask = bRec.typeMask;
perturbedQuery.component = bRec.component;
Spectrum result = m_nested->sample(perturbedQuery, sample);
if (!result.isZero()) {
bRec.sampledComponent = perturbedQuery.sampledComponent;
bRec.sampledType = perturbedQuery.sampledType;
bRec.wo = its.toLocal(perturbed.toWorld(perturbedQuery.wo));
bRec.eta = perturbedQuery.eta;
if (Frame::cosTheta(bRec.wo) * Frame::cosTheta(perturbedQuery.wo) <= 0)
return Spectrum(0.0f);
}
return result;
}
Spectrum sample(BSDFSamplingRecord &bRec, Float &pdf, const Point2 &sample) const {
const Intersection& its = bRec.its;
Intersection perturbed(its);
perturbed.shFrame = getFrame(its);
BSDFSamplingRecord perturbedQuery(perturbed, bRec.sampler, bRec.mode);
perturbedQuery.wi = perturbed.toLocal(its.toWorld(bRec.wi));
perturbedQuery.typeMask = bRec.typeMask;
perturbedQuery.component = bRec.component;
Spectrum result = m_nested->sample(perturbedQuery, pdf, sample);
if (!result.isZero()) {
bRec.sampledComponent = perturbedQuery.sampledComponent;
bRec.sampledType = perturbedQuery.sampledType;
bRec.wo = its.toLocal(perturbed.toWorld(perturbedQuery.wo));
bRec.eta = perturbedQuery.eta;
if (Frame::cosTheta(bRec.wo) * Frame::cosTheta(perturbedQuery.wo) <= 0)
return Spectrum(0.0f);
}
return result;
}
Float getRoughness(const Intersection &its, int component) const {
return m_nested->getRoughness(its, component);
}
std::string toString() const {
std::ostringstream oss;
oss << "NormalMap[" << endl
<< " id = \"" << getID() << "\"," << endl
<< " normals = " << indent(m_normals->toString()) << endl
<< " nested = " << indent(m_nested->toString()) << endl
<< "]";
return oss.str();
}
Shader *createShader(Renderer *renderer) const;
MTS_DECLARE_CLASS()
protected:
ref<Texture> m_normals;
ref<BSDF> m_nested;
};
// ================ Hardware shader implementation ================
/**
* This is a quite approximate version of the bump map model -- it likely
* won't match the reference exactly, but it should be good enough for
* preview purposes
*/
class NormalMapShader : public Shader {
public:
NormalMapShader(Renderer *renderer, const BSDF *nested, const Texture *normals)
: Shader(renderer, EBSDFShader), m_nested(nested), m_normals(normals) {
m_nestedShader = renderer->registerShaderForResource(m_nested.get());
m_normalShader = renderer->registerShaderForResource(m_normals.get());
}
bool isComplete() const {
return m_nestedShader.get() != NULL;
}
void cleanup(Renderer *renderer) {
renderer->unregisterShaderForResource(m_nested.get());
renderer->unregisterShaderForResource(m_normals.get());
}
void putDependencies(std::vector<Shader *> &deps) {
deps.push_back(m_nestedShader.get());
deps.push_back(m_normalShader.get());
}
void generateCode(std::ostringstream &oss,
const std::string &evalName,
const std::vector<std::string> &depNames) const {
oss << "vec3 " << evalName << "(vec2 uv, vec3 wi, vec3 wo) {" << endl
<< " vec3 n = normalize(2.0*" << depNames[1] << "(uv) - vec3(1.0));" << endl
<< " vec3 s = normalize(vec3(1.0-n.x*n.x, -n.x*n.y, -n.x*n.z)); " << endl
<< " vec3 t = cross(s, n);" << endl
<< " wi = vec3(dot(wi, s), dot(wi, t), dot(wi, n));" << endl
<< " wo = vec3(dot(wo, s), dot(wo, t), dot(wo, n));" << endl
<< " return " << depNames[0] << "(uv, wi, wo);" << endl
<< "}" << endl
<< endl
<< "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl
<< " vec3 n = normalize(2.0*" << depNames[1] << "(uv) - vec3(1.0));" << endl
<< " vec3 s = normalize(vec3(1.0-n.x*n.x, -n.x*n.y, -n.x*n.z)); " << endl
<< " vec3 t = cross(s, n);" << endl
<< " wi = vec3(dot(wi, s), dot(wi, t), dot(wi, n));" << endl
<< " wo = vec3(dot(wo, s), dot(wo, t), dot(wo, n));" << endl
<< " return " << depNames[0] << "_diffuse(uv, wi, wo);" << endl
<< "}" << endl
<< endl;
}
MTS_DECLARE_CLASS()
private:
ref<const BSDF> m_nested;
ref<const Texture> m_normals;
ref<Shader> m_nestedShader;
ref<Shader> m_normalShader;
};
Shader *NormalMap::createShader(Renderer *renderer) const {
return new NormalMapShader(renderer, m_nested.get(), m_normals.get());
}
MTS_IMPLEMENT_CLASS(NormalMapShader, false, Shader)
MTS_IMPLEMENT_CLASS_S(NormalMap, false, BSDF)
MTS_EXPORT_PLUGIN(NormalMap, "Smooth dielectric coating");
MTS_NAMESPACE_END

View File

@ -155,7 +155,7 @@ MTS_NAMESPACE_BEGIN
* <!-- Fetch roughness values from a texture and slightly reduce them -->
* <texture type="scale" name="alpha">
* <texture name="alpha" type="bitmap">
* <string name="filename" value="bump.png"/>
* <string name="filename" value="roughness.png"/>
* </texture>
* <float name="scale" value="0.6"/>
* </texture>

View File

@ -632,8 +632,6 @@ void InterpolatedSpectrum::zeroExtend() {
}
Float InterpolatedSpectrum::average(Float lambdaMin, Float lambdaMax) const {
typedef std::vector<Float>::const_iterator iterator;
if (m_wavelengths.size() < 2)
return 0.0f;

View File

@ -63,6 +63,10 @@ Float BSDF::getEta() const {
return 1.0f;
}
Frame BSDF::getFrame(const Intersection &its) const {
return its.shFrame;
}
Float BSDF::getRoughness(const Intersection &its, int component) const {
NotImplementedError("getRoughness");
}

View File

@ -53,6 +53,24 @@ ref<Texture> Texture::expand() {
return this;
}
void Texture::evalGradient(const Intersection &_its, Spectrum *gradient) const {
const Float eps = Epsilon;
Intersection its(_its);
Spectrum value = eval(its, false);
its.p = _its.p + its.dpdu * eps;
its.uv = _its.uv + Point2(eps, 0);
Spectrum valueU = eval(its, false);
its.p = _its.p + its.dpdv * eps;
its.uv = _its.uv + Point2(0, eps);
Spectrum valueV = eval(its, false);
gradient[0] = (valueU - value)*(1/eps);
gradient[1] = (valueV - value)*(1/eps);
}
Texture::~Texture() { }
void Texture::serialize(Stream *stream, InstanceManager *manager) const {
@ -101,6 +119,26 @@ Spectrum Texture2D::eval(const Intersection &its, bool filter) const {
}
}
void Texture2D::evalGradient(const Intersection &its, Spectrum *gradient) const {
Point2 uv = Point2(its.uv.x * m_uvScale.x, its.uv.y * m_uvScale.y) + m_uvOffset;
evalGradient(uv, gradient);
gradient[0] *= m_uvScale.x;
gradient[1] *= m_uvScale.y;
}
void Texture2D::evalGradient(const Point2 &uv, Spectrum *gradient) const {
const Float eps = Epsilon;
Spectrum value = eval(uv);
Spectrum valueU = eval(uv + Vector2(eps, 0));
Spectrum valueV = eval(uv + Vector2(0, eps));
gradient[0] = (valueU - value)*(1/eps);
gradient[1] = (valueV - value)*(1/eps);
}
ref<Bitmap> Texture2D::getBitmap(const Vector2i &sizeHint) const {
Vector2i res(sizeHint);
if (res.x <= 0 || res.y <= 0)

View File

@ -59,6 +59,9 @@ MTS_NAMESPACE_BEGIN
* Specifies an optional linear object-to-world transformation.
* \default{none (i.e. object space $=$ world space)}
* }
* \parameter{collapse}{\Boolean}{
* Collapse all contained meshes into a single object \default{\code{false}}
* }
* }
* \renderings{
* \label{fig:rungholt}
@ -196,6 +199,9 @@ public:
/* Causes all normals to be flipped */
m_flipNormals = props.getBoolean("flipNormals", false);
/* Collapse all contained shapes / groups into a single object? */
m_collapse = props.getBoolean("collapse", false);
/* Causes all texture coordinates to be vertically flipped */
bool flipTexCoords = props.getBoolean("flipTexCoords", true);
@ -238,7 +244,7 @@ public:
Normal n;
iss >> n.x >> n.y >> n.z;
normals.push_back(n);
} else if (buf == "g") {
} else if (buf == "g" && !m_collapse) {
std::string targetName;
std::string newName = trim(line.substr(1, line.length()-1));
@ -816,6 +822,7 @@ private:
bool m_flipNormals, m_faceNormals;
std::string m_name;
AABB m_aabb;
bool m_collapse;
};
MTS_IMPLEMENT_CLASS_S(WavefrontOBJ, false, Shape)

View File

@ -219,6 +219,8 @@ public:
if (filterType == "ewa")
m_filterType = EEWA;
else if (filterType == "bilinear")
m_filterType = EBilinear;
else if (filterType == "trilinear")
m_filterType = ETrilinear;
else if (filterType == "nearest")
@ -451,6 +453,32 @@ public:
return result;
}
void evalGradient(const Point2 &uv, Spectrum *gradient) const {
/* There are no ray differentials to do any kind of
prefiltering. Evaluate the full-resolution texture */
if (m_mipmap3.get()) {
Color3 result[2];
if (m_mipmap3->getFilterType() != ENearest) {
m_mipmap3->evalGradientBilinear(0, uv, result);
gradient[0].fromLinearRGB(result[0][0], result[0][1], result[0][2]);
gradient[1].fromLinearRGB(result[1][0], result[1][1], result[1][2]);
} else {
gradient[0] = gradient[1] = Spectrum(0.0f);
}
} else {
Color1 result[2];
if (m_mipmap1->getFilterType() != ENearest) {
m_mipmap1->evalGradientBilinear(0, uv, result);
gradient[0] = Spectrum(result[0][0]);
gradient[1] = Spectrum(result[1][0]);
} else {
gradient[0] = gradient[1] = Spectrum(0.0f);
}
}
stats::filteredLookups.incrementBase();
}
ref<Bitmap> getBitmap(const Vector2i &/* unused */) const {
return m_mipmap1.get() ? m_mipmap1->toBitmap() : m_mipmap3->toBitmap();
}
@ -599,6 +627,10 @@ public:
case ENearest:
m_gpuTexture->setFilterType(GPUTexture::ENearest);
break;
case EBilinear:
m_gpuTexture->setFilterType(GPUTexture::ELinear);
m_gpuTexture->setMipMapped(false);
break;
default:
m_gpuTexture->setFilterType(GPUTexture::EMipMapLinear);
break;

View File

@ -86,6 +86,12 @@ public:
return m_nested->eval(its, filter) * m_scale;
}
void evalGradient(const Intersection &its, Spectrum *gradient) const {
m_nested->evalGradient(its, gradient);
gradient[0] *= m_scale;
gradient[1] *= m_scale;
}
Spectrum getAverage() const {
return m_nested->getAverage() * m_scale;
}