improved bump and normal mapping support
parent
38fced0f53
commit
b68a38ed9a
|
@ -48,7 +48,7 @@
|
||||||
<xsl:apply-templates select="@*|node()"/>
|
<xsl:apply-templates select="@*|node()"/>
|
||||||
|
|
||||||
<!-- The vertical component of OBJ texture coordinates is now
|
<!-- 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. -->
|
this change for consistency in old scenes. -->
|
||||||
<xsl:if test="@type='obj'">
|
<xsl:if test="@type='obj'">
|
||||||
<boolean name="flipTexCoords" value="false"/>
|
<boolean name="flipTexCoords" value="false"/>
|
||||||
|
@ -66,7 +66,7 @@
|
||||||
<xsl:template match="bsdf[@type='microfacet' or @type='phong' or @type='ward']">
|
<xsl:template match="bsdf[@type='microfacet' or @type='phong' or @type='ward']">
|
||||||
<xsl:variable name="diffuseAmount">
|
<xsl:variable name="diffuseAmount">
|
||||||
<xsl:choose>
|
<xsl:choose>
|
||||||
<xsl:when test="float[@name='diffuseAmount']">
|
<xsl:when test="float[@name='diffuseAmount']">
|
||||||
<xsl:value-of select="float[@name='diffuseAmount']/@value"/>
|
<xsl:value-of select="float[@name='diffuseAmount']/@value"/>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
<xsl:otherwise>1.0</xsl:otherwise>
|
<xsl:otherwise>1.0</xsl:otherwise>
|
||||||
|
@ -74,7 +74,7 @@
|
||||||
</xsl:variable>
|
</xsl:variable>
|
||||||
<xsl:variable name="specularAmount">
|
<xsl:variable name="specularAmount">
|
||||||
<xsl:choose>
|
<xsl:choose>
|
||||||
<xsl:when test="float[@name='specularAmount']">
|
<xsl:when test="float[@name='specularAmount']">
|
||||||
<xsl:value-of select="float[@name='specularAmount']/@value"/>
|
<xsl:value-of select="float[@name='specularAmount']/@value"/>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
<xsl:otherwise>1.0</xsl:otherwise>
|
<xsl:otherwise>1.0</xsl:otherwise>
|
||||||
|
@ -211,7 +211,7 @@
|
||||||
<xsl:template match="float[@name='sizeMultiplier']/@name">
|
<xsl:template match="float[@name='sizeMultiplier']/@name">
|
||||||
<xsl:attribute name="name">densityMultiplier</xsl:attribute>
|
<xsl:attribute name="name">densityMultiplier</xsl:attribute>
|
||||||
</xsl:template>
|
</xsl:template>
|
||||||
|
|
||||||
<!-- There is no more 'mirror' plugin; replace with smooth chrome -->
|
<!-- There is no more 'mirror' plugin; replace with smooth chrome -->
|
||||||
<xsl:template match="bsdf[@type='mirror']/@type">
|
<xsl:template match="bsdf[@type='mirror']/@type">
|
||||||
<xsl:attribute name="type">conductor</xsl:attribute>
|
<xsl:attribute name="type">conductor</xsl:attribute>
|
||||||
|
@ -246,7 +246,7 @@
|
||||||
<xsl:template match="bsdf[@type='composite']/@type">
|
<xsl:template match="bsdf[@type='composite']/@type">
|
||||||
<xsl:attribute name="type">mixturebsdf</xsl:attribute>
|
<xsl:attribute name="type">mixturebsdf</xsl:attribute>
|
||||||
</xsl:template>
|
</xsl:template>
|
||||||
|
|
||||||
<!-- Update the name of the exrtexture plugin -->
|
<!-- Update the name of the exrtexture plugin -->
|
||||||
<xsl:template match="texture[@type='exrtexture']/@type">
|
<xsl:template match="texture[@type='exrtexture']/@type">
|
||||||
<xsl:attribute name="type">bitmap</xsl:attribute>
|
<xsl:attribute name="type">bitmap</xsl:attribute>
|
||||||
|
|
|
@ -91,7 +91,7 @@
|
||||||
<xsl:template match="film[@type='pngfilm']/@type">
|
<xsl:template match="film[@type='pngfilm']/@type">
|
||||||
<xsl:attribute name="type">ldrfilm</xsl:attribute>
|
<xsl:attribute name="type">ldrfilm</xsl:attribute>
|
||||||
</xsl:template>
|
</xsl:template>
|
||||||
|
|
||||||
<!-- Update the 'focusDepth' attribute name -->
|
<!-- Update the 'focusDepth' attribute name -->
|
||||||
<xsl:template match="float[@name='focusDepth']/@name">
|
<xsl:template match="float[@name='focusDepth']/@name">
|
||||||
<xsl:attribute name="name">focusDistance</xsl:attribute>
|
<xsl:attribute name="name">focusDistance</xsl:attribute>
|
||||||
|
|
|
@ -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>
|
Before Width: | Height: | Size: 172 KiB After Width: | Height: | Size: 172 KiB |
Before Width: | Height: | Size: 193 KiB After Width: | Height: | Size: 193 KiB |
|
@ -453,6 +453,15 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual Float getRoughness(const Intersection &its, int index) const;
|
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
|
//! @{ \name ConfigurableObject interface
|
||||||
// =============================================================
|
// =============================================================
|
||||||
|
|
|
@ -52,12 +52,14 @@ namespace stats {
|
||||||
|
|
||||||
/// Specifies the desired antialiasing filter
|
/// Specifies the desired antialiasing filter
|
||||||
enum EMIPFilterType {
|
enum EMIPFilterType {
|
||||||
/// No filtering (i.e. nearest neighbor lookups)
|
/// No filtering, nearest neighbor lookups
|
||||||
ENearest = 0,
|
ENearest = 0,
|
||||||
|
/// No filtering, only bilinear interpolation
|
||||||
|
EBilinear = 1,
|
||||||
/// Basic trilinear filtering
|
/// Basic trilinear filtering
|
||||||
ETrilinear = 1,
|
ETrilinear = 2,
|
||||||
/// Elliptically weighted average
|
/// Elliptically weighted average
|
||||||
EEWA = 2,
|
EEWA = 3
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -179,7 +181,7 @@ public:
|
||||||
/* 1. Determine the number of MIP levels. The following
|
/* 1. Determine the number of MIP levels. The following
|
||||||
code also handles non-power-of-2 input. */
|
code also handles non-power-of-2 input. */
|
||||||
m_levels = 1;
|
m_levels = 1;
|
||||||
if (m_filterType != ENearest) {
|
if (m_filterType != ENearest && m_filterType != EBilinear) {
|
||||||
Vector2i size = bitmap_->getSize();
|
Vector2i size = bitmap_->getSize();
|
||||||
while (size.x > 1 || size.y > 1) {
|
while (size.x > 1 || size.y > 1) {
|
||||||
size.x = std::max(1, (size.x + 1) / 2);
|
size.x = std::max(1, (size.x + 1) / 2);
|
||||||
|
@ -233,7 +235,7 @@ public:
|
||||||
m_sizeRatio[0] = Vector2(1, 1);
|
m_sizeRatio[0] = Vector2(1, 1);
|
||||||
|
|
||||||
/* 3. Progressively downsample until only a 1x1 image is left */
|
/* 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();
|
Vector2i size = bitmap_->getSize();
|
||||||
m_levels = 1;
|
m_levels = 1;
|
||||||
while (size.x > 1 || size.y > 1) {
|
while (size.x > 1 || size.y > 1) {
|
||||||
|
@ -344,7 +346,7 @@ public:
|
||||||
mmapPtr += m_pyramid[0].getBufferSize();
|
mmapPtr += m_pyramid[0].getBufferSize();
|
||||||
m_sizeRatio[0] = Vector2(1, 1);
|
m_sizeRatio[0] = Vector2(1, 1);
|
||||||
|
|
||||||
if (m_filterType != ENearest) {
|
if (m_filterType != ENearest && m_filterType != EBilinear) {
|
||||||
/* Map the remainder of the image pyramid */
|
/* Map the remainder of the image pyramid */
|
||||||
int level = 1;
|
int level = 1;
|
||||||
while (size.x > 1 || size.y > 1) {
|
while (size.x > 1 || size.y > 1) {
|
||||||
|
@ -425,7 +427,7 @@ public:
|
||||||
size_t expectedFileSize = sizeof(MIPMapHeader) + padding
|
size_t expectedFileSize = sizeof(MIPMapHeader) + padding
|
||||||
+ Array2DType::bufferSize(size);
|
+ Array2DType::bufferSize(size);
|
||||||
|
|
||||||
if (filterType != ENearest) {
|
if (filterType != ENearest && filterType != EBilinear) {
|
||||||
while (size.x > 1 || size.y > 1) {
|
while (size.x > 1 || size.y > 1) {
|
||||||
size.x = std::max(1, (size.x + 1) / 2);
|
size.x = std::max(1, (size.x + 1) / 2);
|
||||||
size.y = std::max(1, (size.y + 1) / 2);
|
size.y = std::max(1, (size.y + 1) / 2);
|
||||||
|
@ -586,10 +588,42 @@ public:
|
||||||
+ evalTexel(level, xPos + 1, yPos + 1) * dx1 * dy1;
|
+ 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
|
/// \brief Perform a filtered texture lookup using the configured method
|
||||||
Value eval(const Point2 &uv, const Vector2 &d0, const Vector2 &d1) const {
|
Value eval(const Point2 &uv, const Vector2 &d0, const Vector2 &d1) const {
|
||||||
if (m_filterType == ENearest)
|
if (m_filterType == ENearest)
|
||||||
return evalBox(0, uv);
|
return evalBox(0, uv);
|
||||||
|
else if (m_filterType == EBilinear)
|
||||||
|
return evalBilinear(0, uv);
|
||||||
|
|
||||||
/* Convert into texel coordinates */
|
/* Convert into texel coordinates */
|
||||||
const Vector2i &size = m_pyramid[0].getSize();
|
const Vector2i &size = m_pyramid[0].getSize();
|
||||||
|
@ -685,6 +719,7 @@ public:
|
||||||
|
|
||||||
switch (m_filterType) {
|
switch (m_filterType) {
|
||||||
case ENearest: oss << "nearest," << endl; break;
|
case ENearest: oss << "nearest," << endl; break;
|
||||||
|
case EBilinear: oss << "bilinear," << endl; break;
|
||||||
case ETrilinear: oss << "trilinear," << endl; break;
|
case ETrilinear: oss << "trilinear," << endl; break;
|
||||||
case EEWA: oss << "ewa," << endl; break;
|
case EEWA: oss << "ewa," << endl; break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,16 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual Spectrum eval(const Intersection &its, bool filter = true) const;
|
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
|
/// Return the component-wise average value of the texture over its domain
|
||||||
virtual Spectrum getAverage() const;
|
virtual Spectrum getAverage() const;
|
||||||
|
|
||||||
|
@ -108,6 +118,16 @@ public:
|
||||||
*/
|
*/
|
||||||
Spectrum eval(const Intersection &its, bool filter = true) const;
|
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
|
/// Serialize to a binary data stream
|
||||||
virtual void serialize(Stream *stream, InstanceManager *manager) const;
|
virtual void serialize(Stream *stream, InstanceManager *manager) const;
|
||||||
|
|
||||||
|
@ -118,6 +138,9 @@ public:
|
||||||
virtual Spectrum eval(const Point2 &uv, const Vector2 &d0,
|
virtual Spectrum eval(const Point2 &uv, const Vector2 &d0,
|
||||||
const Vector2 &d1) const = 0;
|
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
|
* \brief Return a bitmap representation of the texture
|
||||||
*
|
*
|
||||||
|
|
|
@ -18,7 +18,8 @@ add_bsdf(roughconductor roughconductor.cpp microfacet.h)
|
||||||
add_bsdf(roughplastic roughplastic.cpp microfacet.h ior.h)
|
add_bsdf(roughplastic roughplastic.cpp microfacet.h ior.h)
|
||||||
|
|
||||||
# Materials that act as modifiers
|
# 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(twosided twosided.cpp)
|
||||||
add_bsdf(mask mask.cpp)
|
add_bsdf(mask mask.cpp)
|
||||||
add_bsdf(mixturebsdf mixturebsdf.cpp)
|
add_bsdf(mixturebsdf mixturebsdf.cpp)
|
||||||
|
|
|
@ -17,7 +17,8 @@ plugins += env.SharedLibrary('mixturebsdf', ['mixturebsdf.cpp'])
|
||||||
plugins += env.SharedLibrary('blendbsdf', ['blendbsdf.cpp'])
|
plugins += env.SharedLibrary('blendbsdf', ['blendbsdf.cpp'])
|
||||||
plugins += env.SharedLibrary('coating', ['coating.cpp'])
|
plugins += env.SharedLibrary('coating', ['coating.cpp'])
|
||||||
plugins += env.SharedLibrary('roughcoating', ['roughcoating.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
|
# Other materials
|
||||||
plugins += env.SharedLibrary('ward', ['ward.cpp'])
|
plugins += env.SharedLibrary('ward', ['ward.cpp'])
|
||||||
|
|
|
@ -21,9 +21,9 @@
|
||||||
|
|
||||||
MTS_NAMESPACE_BEGIN
|
MTS_NAMESPACE_BEGIN
|
||||||
|
|
||||||
/*! \plugin{bump}{Bump map modifier}
|
/*! \plugin{bumpmap}{Bump map modifier}
|
||||||
* \order{12}
|
* \order{12}
|
||||||
* \icon{bsdf_bump}
|
* \icon{bsdf_bumpmap}
|
||||||
*
|
*
|
||||||
* \parameters{
|
* \parameters{
|
||||||
* \parameter{\Unnamed}{\Texture}{
|
* \parameter{\Unnamed}{\Texture}{
|
||||||
|
@ -35,8 +35,8 @@ MTS_NAMESPACE_BEGIN
|
||||||
* be affected by the bump map}
|
* be affected by the bump map}
|
||||||
* }
|
* }
|
||||||
* \renderings{
|
* \renderings{
|
||||||
* \rendering{Bump map based on tileable diagonal lines}{bsdf_bump_1}
|
* \rendering{Bump map based on tileable diagonal lines}{bsdf_bumpmap_1}
|
||||||
* \rendering{An irregular bump map}{bsdf_bump_2}
|
* \rendering{An irregular bump map}{bsdf_bumpmap_2}
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* Bump mapping \cite{Blinn1978Simulation} is a simple technique for cheaply
|
* 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
|
* texture plugin can be used to magnify or reduce the effect of a
|
||||||
* bump map texture.
|
* bump map texture.
|
||||||
* \begin{xml}[caption=A rough metal model with a scaled image-based bump map]
|
* \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 -->
|
* <!-- The bump map is applied to a rough metal BRDF -->
|
||||||
* <bsdf type="roughconductor"/>
|
* <bsdf type="roughconductor"/>
|
||||||
*
|
*
|
||||||
|
@ -119,22 +119,12 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void perturbIntersection(const Intersection &its, Intersection &target) const {
|
Frame getFrame(const Intersection &its) const {
|
||||||
const Float eps = Epsilon;
|
Spectrum grad[2];
|
||||||
|
m_displacement->evalGradient(its, grad);
|
||||||
|
|
||||||
/* Compute the U and V displacementment derivatives */
|
Float dDispDu = grad[0].getLuminance();
|
||||||
target = its;
|
Float dDispDv = grad[1].getLuminance();
|
||||||
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;
|
|
||||||
|
|
||||||
/* Build a perturbed frame -- ignores the usually
|
/* Build a perturbed frame -- ignores the usually
|
||||||
negligible normal derivative term */
|
negligible normal derivative term */
|
||||||
|
@ -143,21 +133,22 @@ public:
|
||||||
Vector dpdv = its.dpdv + its.shFrame.n * (
|
Vector dpdv = its.dpdv + its.shFrame.n * (
|
||||||
dDispDv - dot(its.shFrame.n, its.dpdv));
|
dDispDv - dot(its.shFrame.n, its.dpdv));
|
||||||
|
|
||||||
dpdu = normalize(dpdu);
|
Frame result;
|
||||||
dpdv = normalize(dpdv - dpdu * dot(dpdv, dpdu));
|
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;
|
if (dot(result.n, its.geoFrame.n) < 0)
|
||||||
target.shFrame.t = dpdv;
|
result.n *= -1;
|
||||||
target.shFrame = Frame(Normal(cross(dpdv, dpdu)));
|
|
||||||
|
|
||||||
if (dot(target.shFrame.n, target.geoFrame.n) < 0)
|
return result;
|
||||||
target.shFrame.n *= -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Spectrum eval(const BSDFSamplingRecord &bRec, EMeasure measure) const {
|
Spectrum eval(const BSDFSamplingRecord &bRec, EMeasure measure) const {
|
||||||
const Intersection& its = bRec.its;
|
const Intersection& its = bRec.its;
|
||||||
Intersection perturbed;
|
Intersection perturbed(its);
|
||||||
perturbIntersection(its, perturbed);
|
perturbed.shFrame = getFrame(its);
|
||||||
|
|
||||||
BSDFSamplingRecord perturbedQuery(perturbed,
|
BSDFSamplingRecord perturbedQuery(perturbed,
|
||||||
perturbed.toLocal(its.toWorld(bRec.wi)),
|
perturbed.toLocal(its.toWorld(bRec.wi)),
|
||||||
|
@ -172,8 +163,8 @@ public:
|
||||||
|
|
||||||
Float pdf(const BSDFSamplingRecord &bRec, EMeasure measure) const {
|
Float pdf(const BSDFSamplingRecord &bRec, EMeasure measure) const {
|
||||||
const Intersection& its = bRec.its;
|
const Intersection& its = bRec.its;
|
||||||
Intersection perturbed;
|
Intersection perturbed(its);
|
||||||
perturbIntersection(its, perturbed);
|
perturbed.shFrame = getFrame(its);
|
||||||
|
|
||||||
BSDFSamplingRecord perturbedQuery(perturbed,
|
BSDFSamplingRecord perturbedQuery(perturbed,
|
||||||
perturbed.toLocal(its.toWorld(bRec.wi)),
|
perturbed.toLocal(its.toWorld(bRec.wi)),
|
||||||
|
@ -189,8 +180,8 @@ public:
|
||||||
|
|
||||||
Spectrum sample(BSDFSamplingRecord &bRec, const Point2 &sample) const {
|
Spectrum sample(BSDFSamplingRecord &bRec, const Point2 &sample) const {
|
||||||
const Intersection& its = bRec.its;
|
const Intersection& its = bRec.its;
|
||||||
Intersection perturbed;
|
Intersection perturbed(its);
|
||||||
perturbIntersection(its, perturbed);
|
perturbed.shFrame = getFrame(its);
|
||||||
|
|
||||||
BSDFSamplingRecord perturbedQuery(perturbed, bRec.sampler, bRec.mode);
|
BSDFSamplingRecord perturbedQuery(perturbed, bRec.sampler, bRec.mode);
|
||||||
perturbedQuery.wi = perturbed.toLocal(its.toWorld(bRec.wi));
|
perturbedQuery.wi = perturbed.toLocal(its.toWorld(bRec.wi));
|
||||||
|
@ -202,6 +193,7 @@ public:
|
||||||
bRec.sampledComponent = perturbedQuery.sampledComponent;
|
bRec.sampledComponent = perturbedQuery.sampledComponent;
|
||||||
bRec.sampledType = perturbedQuery.sampledType;
|
bRec.sampledType = perturbedQuery.sampledType;
|
||||||
bRec.wo = its.toLocal(perturbed.toWorld(perturbedQuery.wo));
|
bRec.wo = its.toLocal(perturbed.toWorld(perturbedQuery.wo));
|
||||||
|
bRec.eta = perturbedQuery.eta;
|
||||||
if (Frame::cosTheta(bRec.wo) * Frame::cosTheta(perturbedQuery.wo) <= 0)
|
if (Frame::cosTheta(bRec.wo) * Frame::cosTheta(perturbedQuery.wo) <= 0)
|
||||||
return Spectrum(0.0f);
|
return Spectrum(0.0f);
|
||||||
}
|
}
|
||||||
|
@ -210,8 +202,8 @@ public:
|
||||||
|
|
||||||
Spectrum sample(BSDFSamplingRecord &bRec, Float &pdf, const Point2 &sample) const {
|
Spectrum sample(BSDFSamplingRecord &bRec, Float &pdf, const Point2 &sample) const {
|
||||||
const Intersection& its = bRec.its;
|
const Intersection& its = bRec.its;
|
||||||
Intersection perturbed;
|
Intersection perturbed(its);
|
||||||
perturbIntersection(its, perturbed);
|
perturbed.shFrame = getFrame(its);
|
||||||
|
|
||||||
BSDFSamplingRecord perturbedQuery(perturbed, bRec.sampler, bRec.mode);
|
BSDFSamplingRecord perturbedQuery(perturbed, bRec.sampler, bRec.mode);
|
||||||
perturbedQuery.wi = perturbed.toLocal(its.toWorld(bRec.wi));
|
perturbedQuery.wi = perturbed.toLocal(its.toWorld(bRec.wi));
|
||||||
|
@ -223,6 +215,7 @@ public:
|
||||||
bRec.sampledComponent = perturbedQuery.sampledComponent;
|
bRec.sampledComponent = perturbedQuery.sampledComponent;
|
||||||
bRec.sampledType = perturbedQuery.sampledType;
|
bRec.sampledType = perturbedQuery.sampledType;
|
||||||
bRec.wo = its.toLocal(perturbed.toWorld(perturbedQuery.wo));
|
bRec.wo = its.toLocal(perturbed.toWorld(perturbedQuery.wo));
|
||||||
|
bRec.eta = perturbedQuery.eta;
|
||||||
if (Frame::cosTheta(bRec.wo) * Frame::cosTheta(perturbedQuery.wo) <= 0)
|
if (Frame::cosTheta(bRec.wo) * Frame::cosTheta(perturbedQuery.wo) <= 0)
|
||||||
return Spectrum(0.0f);
|
return Spectrum(0.0f);
|
||||||
}
|
}
|
||||||
|
@ -285,38 +278,32 @@ public:
|
||||||
const std::string &evalName,
|
const std::string &evalName,
|
||||||
const std::vector<std::string> &depNames) const {
|
const std::vector<std::string> &depNames) const {
|
||||||
oss << "vec3 " << evalName << "(vec2 uv, vec3 wi, vec3 wo) {" << endl
|
oss << "vec3 " << evalName << "(vec2 uv, vec3 wi, vec3 wo) {" << endl
|
||||||
<< " float du = abs(dFdx(uv.x)), dv = abs(dFdx(uv.y));" << endl
|
<< " float eps = 1e-4;" << endl
|
||||||
<< " if (du == 0.0) du = 0.001;" << endl
|
|
||||||
<< " if (dv == 0.0) dv = 0.001;" << endl
|
|
||||||
<< " float displacement = " << depNames[1] << "(uv)[0];" << endl
|
<< " float displacement = " << depNames[1] << "(uv)[0];" << endl
|
||||||
<< " float displacementU = " << depNames[1] << "(uv + vec2(du, 0.0))[0];" << endl
|
<< " float displacementU = " << depNames[1] << "(uv + vec2(eps, 0.0))[0];" << endl
|
||||||
<< " float displacementV = " << depNames[1] << "(uv + vec2(0.0, dv))[0];" << endl
|
<< " float displacementV = " << depNames[1] << "(uv + vec2(0.0, eps))[0];" << endl
|
||||||
<< " float dfdu = (displacementU - displacement)/du;" << endl
|
<< " float dfdu = (displacementU - displacement)*(0.5/eps);" << endl
|
||||||
<< " float dfdv = (displacementV - displacement)/dv;" << endl
|
<< " float dfdv = (displacementV - displacement)*(0.5/eps);" << endl
|
||||||
<< " vec3 dpdu = normalize(vec3(1.0, 0.0, dfdu));" << endl
|
<< " vec3 n = normalize(vec3(-dfdu, -dfdv, 1.0));" << endl
|
||||||
<< " vec3 dpdv = vec3(0.0, 1.0, dfdv);" << endl
|
<< " vec3 s = normalize(vec3(1.0-n.x*n.x, -n.x*n.y, -n.x*n.z)); " << endl
|
||||||
<< " dpdv = normalize(dpdv - dot(dpdu, dpdv)*dpdu);" << endl
|
<< " vec3 t = cross(s, n);" << endl
|
||||||
<< " vec3 n = cross(dpdu, dpdv);" << endl
|
<< " wi = vec3(dot(wi, s), dot(wi, t), dot(wi, n));" << endl
|
||||||
<< " wi = vec3(dot(wi, dpdu), dot(wi, dpdv), dot(wi, n));" << endl
|
<< " wo = vec3(dot(wo, s), dot(wo, t), dot(wo, n));" << endl
|
||||||
<< " wo = vec3(dot(wo, dpdu), dot(wo, dpdv), dot(wo, n));" << endl
|
|
||||||
<< " return " << depNames[0] << "(uv, wi, wo);" << endl
|
<< " return " << depNames[0] << "(uv, wi, wo);" << endl
|
||||||
<< "}" << endl
|
<< "}" << endl
|
||||||
<< endl
|
<< endl
|
||||||
<< "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl
|
<< "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl
|
||||||
<< " float du = abs(dFdx(uv.x)), dv = abs(dFdx(uv.y));" << endl
|
<< " float eps = 1e-4;" << endl
|
||||||
<< " if (du == 0.0) du = 0.001;" << endl
|
|
||||||
<< " if (dv == 0.0) dv = 0.001;" << endl
|
|
||||||
<< " float displacement = " << depNames[1] << "(uv)[0];" << endl
|
<< " float displacement = " << depNames[1] << "(uv)[0];" << endl
|
||||||
<< " float displacementU = " << depNames[1] << "(uv + vec2(du, 0.0))[0];" << endl
|
<< " float displacementU = " << depNames[1] << "(uv + vec2(eps, 0.0))[0];" << endl
|
||||||
<< " float displacementV = " << depNames[1] << "(uv + vec2(0.0, dv))[0];" << endl
|
<< " float displacementV = " << depNames[1] << "(uv + vec2(0.0, eps))[0];" << endl
|
||||||
<< " float dfdu = (displacementU - displacement)/du;" << endl
|
<< " float dfdu = (displacementU - displacement)*(0.5/eps);" << endl
|
||||||
<< " float dfdv = (displacementV - displacement)/dv;" << endl
|
<< " float dfdv = (displacementV - displacement)*(0.5/eps);" << endl
|
||||||
<< " vec3 dpdu = normalize(vec3(1.0, 0.0, dfdu));" << endl
|
<< " vec3 n = normalize(vec3(-dfdu, -dfdv, 1.0));" << endl
|
||||||
<< " vec3 dpdv = vec3(0.0, 1.0, dfdv);" << endl
|
<< " vec3 s = normalize(vec3(1.0-n.x*n.x, -n.x*n.y, -n.x*n.z)); " << endl
|
||||||
<< " dpdv = normalize(dpdv - dot(dpdu, dpdv)*dpdu);" << endl
|
<< " vec3 t = cross(s, n);" << endl
|
||||||
<< " vec3 n = cross(dpdu, dpdv);" << endl
|
<< " wi = vec3(dot(wi, s), dot(wi, t), dot(wi, n));" << endl
|
||||||
<< " wi = vec3(dot(wi, dpdu), dot(wi, dpdv), dot(wi, n));" << endl
|
<< " wo = vec3(dot(wo, s), dot(wo, t), dot(wo, n));" << endl
|
||||||
<< " wo = vec3(dot(wo, dpdu), dot(wo, dpdv), dot(wo, n));" << endl
|
|
||||||
<< " return " << depNames[0] << "_diffuse(uv, wi, wo);" << endl
|
<< " return " << depNames[0] << "_diffuse(uv, wi, wo);" << endl
|
||||||
<< "}" << endl;
|
<< "}" << endl;
|
||||||
}
|
}
|
||||||
|
@ -335,5 +322,5 @@ Shader *BumpMap::createShader(Renderer *renderer) const {
|
||||||
|
|
||||||
MTS_IMPLEMENT_CLASS(BumpMapShader, false, Shader)
|
MTS_IMPLEMENT_CLASS(BumpMapShader, false, Shader)
|
||||||
MTS_IMPLEMENT_CLASS_S(BumpMap, false, BSDF)
|
MTS_IMPLEMENT_CLASS_S(BumpMap, false, BSDF)
|
||||||
MTS_EXPORT_PLUGIN(BumpMap, "Smooth dielectric coating");
|
MTS_EXPORT_PLUGIN(BumpMap, "Bump map modifier");
|
||||||
MTS_NAMESPACE_END
|
MTS_NAMESPACE_END
|
|
@ -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
|
|
@ -155,7 +155,7 @@ MTS_NAMESPACE_BEGIN
|
||||||
* <!-- Fetch roughness values from a texture and slightly reduce them -->
|
* <!-- Fetch roughness values from a texture and slightly reduce them -->
|
||||||
* <texture type="scale" name="alpha">
|
* <texture type="scale" name="alpha">
|
||||||
* <texture name="alpha" type="bitmap">
|
* <texture name="alpha" type="bitmap">
|
||||||
* <string name="filename" value="bump.png"/>
|
* <string name="filename" value="roughness.png"/>
|
||||||
* </texture>
|
* </texture>
|
||||||
* <float name="scale" value="0.6"/>
|
* <float name="scale" value="0.6"/>
|
||||||
* </texture>
|
* </texture>
|
||||||
|
|
|
@ -632,8 +632,6 @@ void InterpolatedSpectrum::zeroExtend() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Float InterpolatedSpectrum::average(Float lambdaMin, Float lambdaMax) const {
|
Float InterpolatedSpectrum::average(Float lambdaMin, Float lambdaMax) const {
|
||||||
typedef std::vector<Float>::const_iterator iterator;
|
|
||||||
|
|
||||||
if (m_wavelengths.size() < 2)
|
if (m_wavelengths.size() < 2)
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
|
|
||||||
|
|
|
@ -63,6 +63,10 @@ Float BSDF::getEta() const {
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Frame BSDF::getFrame(const Intersection &its) const {
|
||||||
|
return its.shFrame;
|
||||||
|
}
|
||||||
|
|
||||||
Float BSDF::getRoughness(const Intersection &its, int component) const {
|
Float BSDF::getRoughness(const Intersection &its, int component) const {
|
||||||
NotImplementedError("getRoughness");
|
NotImplementedError("getRoughness");
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,24 @@ ref<Texture> Texture::expand() {
|
||||||
return this;
|
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() { }
|
Texture::~Texture() { }
|
||||||
|
|
||||||
void Texture::serialize(Stream *stream, InstanceManager *manager) const {
|
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 {
|
ref<Bitmap> Texture2D::getBitmap(const Vector2i &sizeHint) const {
|
||||||
Vector2i res(sizeHint);
|
Vector2i res(sizeHint);
|
||||||
if (res.x <= 0 || res.y <= 0)
|
if (res.x <= 0 || res.y <= 0)
|
||||||
|
|
|
@ -59,6 +59,9 @@ MTS_NAMESPACE_BEGIN
|
||||||
* Specifies an optional linear object-to-world transformation.
|
* Specifies an optional linear object-to-world transformation.
|
||||||
* \default{none (i.e. object space $=$ world space)}
|
* \default{none (i.e. object space $=$ world space)}
|
||||||
* }
|
* }
|
||||||
|
* \parameter{collapse}{\Boolean}{
|
||||||
|
* Collapse all contained meshes into a single object \default{\code{false}}
|
||||||
|
* }
|
||||||
* }
|
* }
|
||||||
* \renderings{
|
* \renderings{
|
||||||
* \label{fig:rungholt}
|
* \label{fig:rungholt}
|
||||||
|
@ -196,6 +199,9 @@ public:
|
||||||
/* Causes all normals to be flipped */
|
/* Causes all normals to be flipped */
|
||||||
m_flipNormals = props.getBoolean("flipNormals", false);
|
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 */
|
/* Causes all texture coordinates to be vertically flipped */
|
||||||
bool flipTexCoords = props.getBoolean("flipTexCoords", true);
|
bool flipTexCoords = props.getBoolean("flipTexCoords", true);
|
||||||
|
|
||||||
|
@ -238,7 +244,7 @@ public:
|
||||||
Normal n;
|
Normal n;
|
||||||
iss >> n.x >> n.y >> n.z;
|
iss >> n.x >> n.y >> n.z;
|
||||||
normals.push_back(n);
|
normals.push_back(n);
|
||||||
} else if (buf == "g") {
|
} else if (buf == "g" && !m_collapse) {
|
||||||
std::string targetName;
|
std::string targetName;
|
||||||
std::string newName = trim(line.substr(1, line.length()-1));
|
std::string newName = trim(line.substr(1, line.length()-1));
|
||||||
|
|
||||||
|
@ -816,6 +822,7 @@ private:
|
||||||
bool m_flipNormals, m_faceNormals;
|
bool m_flipNormals, m_faceNormals;
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
AABB m_aabb;
|
AABB m_aabb;
|
||||||
|
bool m_collapse;
|
||||||
};
|
};
|
||||||
|
|
||||||
MTS_IMPLEMENT_CLASS_S(WavefrontOBJ, false, Shape)
|
MTS_IMPLEMENT_CLASS_S(WavefrontOBJ, false, Shape)
|
||||||
|
|
|
@ -219,6 +219,8 @@ public:
|
||||||
|
|
||||||
if (filterType == "ewa")
|
if (filterType == "ewa")
|
||||||
m_filterType = EEWA;
|
m_filterType = EEWA;
|
||||||
|
else if (filterType == "bilinear")
|
||||||
|
m_filterType = EBilinear;
|
||||||
else if (filterType == "trilinear")
|
else if (filterType == "trilinear")
|
||||||
m_filterType = ETrilinear;
|
m_filterType = ETrilinear;
|
||||||
else if (filterType == "nearest")
|
else if (filterType == "nearest")
|
||||||
|
@ -451,6 +453,32 @@ public:
|
||||||
return result;
|
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 {
|
ref<Bitmap> getBitmap(const Vector2i &/* unused */) const {
|
||||||
return m_mipmap1.get() ? m_mipmap1->toBitmap() : m_mipmap3->toBitmap();
|
return m_mipmap1.get() ? m_mipmap1->toBitmap() : m_mipmap3->toBitmap();
|
||||||
}
|
}
|
||||||
|
@ -599,6 +627,10 @@ public:
|
||||||
case ENearest:
|
case ENearest:
|
||||||
m_gpuTexture->setFilterType(GPUTexture::ENearest);
|
m_gpuTexture->setFilterType(GPUTexture::ENearest);
|
||||||
break;
|
break;
|
||||||
|
case EBilinear:
|
||||||
|
m_gpuTexture->setFilterType(GPUTexture::ELinear);
|
||||||
|
m_gpuTexture->setMipMapped(false);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
m_gpuTexture->setFilterType(GPUTexture::EMipMapLinear);
|
m_gpuTexture->setFilterType(GPUTexture::EMipMapLinear);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -86,6 +86,12 @@ public:
|
||||||
return m_nested->eval(its, filter) * m_scale;
|
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 {
|
Spectrum getAverage() const {
|
||||||
return m_nested->getAverage() * m_scale;
|
return m_nested->getAverage() * m_scale;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue