/*
This file is part of Mitsuba, a physically based rendering system.
Copyright (c) 2007-2011 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 .
*/
#include
#include
MTS_NAMESPACE_BEGIN
static StatsCounter mipmapLookups("Texture", "Mip-map texture lookups");
static StatsCounter ewaLookups("Texture", "EWA texture lookups");
/* Isotropic/anisotropic EWA mip-map texture map class based on PBRT */
MIPMap::MIPMap(int width, int height, Spectrum *pixels,
EFilterType filterType, EWrapMode wrapMode, Float maxAnisotropy)
: m_width(width), m_height(height), m_filterType(filterType),
m_wrapMode(wrapMode), m_maxAnisotropy(maxAnisotropy) {
Spectrum *texture = pixels;
if (filterType != ENone && (!isPow2(width) || !isPow2(height))) {
m_width = (int) roundToPow2((uint32_t) width);
m_height = (int) roundToPow2((uint32_t) height);
/* The texture needs to be up-sampled */
Spectrum *texture1 = new Spectrum[m_width*height];
/* Re-sample into the X direction */
ResampleWeight *weights = resampleWeights(width, m_width);
for (int y=0; y= height) {
if (wrapMode == ERepeat)
pos = modulo(pos, width);
else if (wrapMode == EClamp)
pos = clamp(pos, 0, width-1);
}
if (pos >= 0 && pos < width)
texture1[x+m_width*y] += pixels[pos+y*width]
* weights[x].weight[j];
}
}
}
delete[] weights;
delete[] pixels;
/* Re-sample into the Y direction */
texture = new Spectrum[m_width*m_height];
weights = resampleWeights(height, m_height);
memset(texture, 0, sizeof(Spectrum)*m_width*m_height);
for (int x=0; x= height) {
if (wrapMode == ERepeat)
pos = modulo(pos, height);
else if (wrapMode == EClamp)
pos = clamp(pos, 0, height-1);
}
if (pos >= 0 && pos < height)
texture[x+m_width*y] += texture1[x+pos*m_width]
* weights[y].weight[j];
}
}
}
for (int y=0; y(allocAligned(sizeof(Float)*MIPMAP_LUTSIZE));
for (int i=0; i MIPMap::fromBitmap(Bitmap *bitmap, EFilterType filterType,
EWrapMode wrapMode, Float maxAnisotropy,
Spectrum::EConversionIntent intent) {
int width = bitmap->getWidth();
int height = bitmap->getHeight();
float *data = bitmap->getFloatData();
Spectrum s, *pixels = new Spectrum[width*height];
for (int y=0; y= oldRes);
Float filterWidth = 2.0f;
ResampleWeight *weights = new ResampleWeight[newRes];
for (int i=0; i= levelWidth || y >= levelHeight) {
switch (m_wrapMode) {
case ERepeat:
x = modulo(x, levelWidth);
y = modulo(y, levelHeight);
break;
case EClamp:
x = clamp(x, 0, levelWidth - 1);
y = clamp(y, 0, levelHeight - 1);
break;
case EBlack:
return Spectrum(0.0f);
case EWhite:
return Spectrum(1.0f);
}
}
return m_pyramid[level][x + levelWidth*y];
}
Spectrum MIPMap::triangle(int level, Float x, Float y) const {
if (m_filterType == ENone) {
int xPos = floorToInt(x*m_levelWidth[0]),
yPos = floorToInt(y*m_levelHeight[0]);
return getTexel(0, xPos, yPos);
} else {
level = clamp(level, 0, m_levels - 1);
x = x * m_levelWidth[level] - 0.5f;
y = y * m_levelHeight[level] - 0.5f;
int xPos = floorToInt(x), yPos = floorToInt(y);
Float dx = x - xPos, dy = y - yPos;
return getTexel(level, xPos, yPos) * (1.0f - dx) * (1.0f - dy)
+ getTexel(level, xPos, yPos + 1) * (1.0f - dx) * dy
+ getTexel(level, xPos + 1, yPos) * dx * (1.0f - dy)
+ getTexel(level, xPos + 1, yPos + 1) * dx * dy;
}
}
Spectrum MIPMap::getValue(Float u, Float v,
Float dudx, Float dudy, Float dvdx, Float dvdy) const {
if (m_filterType == ETrilinear) {
++mipmapLookups;
/* Conservatively estimate a square lookup region */
Float width = 2.0f * std::max(
std::max(std::abs(dudx), std::abs(dudy)),
std::max(std::abs(dvdx), std::abs(dvdy)));
Float mipmapLevel = m_levels - 1 +
log2(std::max(width, (Float) 1e-8f));
if (mipmapLevel < 0) {
/* The lookup is smaller than one pixel */
return triangle(0, u, v);
} else if (mipmapLevel >= m_levels - 1) {
/* The lookup is larger than the whole texture */
return getTexel(m_levels - 1, 0, 0);
} else {
/* Tri-linear interpolation */
int level = (int) mipmapLevel;
Float delta = mipmapLevel - level;
return triangle(level, u, v) * (1.0f - delta)
+ triangle(level, u, v) * delta;
}
} else if (m_filterType == EEWA) {
if (dudx*dudx + dudy*dudy < dvdx*dvdx + dvdy*dvdy) {
std::swap(dudx, dvdx);
std::swap(dudy, dvdy);
}
Float majorLength = std::sqrt(dudx * dudx + dudy * dudy);
Float minorLength = std::sqrt(dvdx * dvdx + dvdy * dvdy);
if (minorLength * m_maxAnisotropy < majorLength && minorLength > 0.0f) {
Float scale = majorLength / (minorLength * m_maxAnisotropy);
dvdx *= scale; dvdy *= scale;
minorLength *= scale;
}
if (minorLength == 0)
return triangle(0, u, v);
// The min() below avoids overflow in the int conversion when lod=inf
Float lod =
std::min(std::max((Float) 0, m_levels - 1 + log2(minorLength)),
(Float) (m_levels-1));
int ilod = floorToInt(lod);
Float d = lod - ilod;
return EWA(u, v, dudx, dudy, dvdx, dvdy, ilod) * (1-d) +
EWA(u, v, dudx, dudy, dvdx, dvdy, ilod+1) * d;
} else {
int xPos = floorToInt(u*m_levelWidth[0]),
yPos = floorToInt(v*m_levelHeight[0]);
return getTexel(0, xPos, yPos);
}
}
Spectrum MIPMap::EWA(Float u, Float v, Float dudx, Float dudy, Float dvdx,
Float dvdy, int level) const {
++ewaLookups;
if (level >= m_levels)
return getTexel(m_levels-1, 0, 0);
Spectrum result(0.0f);
Float denominator = 0.0f;
u = u * m_levelWidth[level]; v = v * m_levelHeight[level];
dudx = dudx * m_levelWidth[level]; dudy = dudy * m_levelHeight[level];
dvdx = dvdx * m_levelWidth[level]; dvdy = dvdy * m_levelHeight[level];
Float A = dudy * dudy + dvdy * dvdy + 1.0f;
Float B = -2.0f * (dudx * dudy + dvdx * dvdy);
Float C = dudx * dudx + dvdx * dvdx + 1.0f;
Float F = A * C - B * B * 0.25f;
Float du = std::sqrt(C), dv = std::sqrt(A);
int u0 = (int) std::ceil(u - du);
int u1 = (int) std::floor(u + du);
int v0 = (int) std::ceil(v - dv);
int v1 = (int) std::floor(v + dv);
Float invF = 1.0f / F;
A *= invF; B *= invF; C *= invF;
for (int ut = u0; ut <= u1; ++ut) {
const Float uu = ut - u;
for (int vt = v0; vt <= v1; ++vt) {
const Float vv = vt - v;
const Float r2 = A*uu*uu + B*uu*vv + C*vv*vv;
if (r2 < 1) {
const Float weight = m_weightLut[
std::max(0, std::min((int) (r2 * MIPMAP_LUTSIZE), MIPMAP_LUTSIZE - 1))];
result += getTexel(level, ut, vt) * weight;
denominator += weight;
}
}
}
return result / denominator;
}
Bitmap *MIPMap::getBitmap() const {
Bitmap *bitmap = new Bitmap(m_width, m_height, 128);
float *floatData = bitmap->getFloatData();
Spectrum *specData = m_pyramid[0];
for (int y=0; ytoLinearRGB(r, g, b);
*floatData++ = r;
*floatData++ = g;
*floatData++ = b;
*floatData++ = 1.0f;
}
}
return bitmap;
}
Bitmap *MIPMap::getLDRBitmap() const {
Bitmap *bitmap = new Bitmap(m_width, m_height, 24);
uint8_t *data = bitmap->getData();
Spectrum *specData = m_pyramid[0];
for (int y=0; ytoLinearRGB(r, g, b);
*data++ = (uint8_t) std::min(255, std::max(0, (int) (r*255)));
*data++ = (uint8_t) std::min(255, std::max(0, (int) (g*255)));
*data++ = (uint8_t) std::min(255, std::max(0, (int) (b*255)));
}
}
return bitmap;
}
MTS_IMPLEMENT_CLASS(MIPMap, false, Object)
MTS_NAMESPACE_END