#include MTS_NAMESPACE_BEGIN /* Isotropic/anisotropic EWA mip-map texture map class based on PBRT */ MIPMap::MIPMap(int width, int height, Spectrum *pixels, bool isotropic, EWrapMode wrapMode, Float maxAnisotropy) : m_width(width), m_height(height), m_isotropic(isotropic), m_wrapMode(wrapMode), m_maxAnisotropy(maxAnisotropy) { Spectrum *texture = pixels; if (!isPowerOfTwo(width) || !isPowerOfTwo(height)) { m_width = roundToPowerOfTwo(width); m_height = roundToPowerOfTwo(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= 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); for (int x=0; x= 0 && pos < height) texture[x+m_width*y] += texture1[x+pos*m_width] * weights[y].weight[j]; } } for (int y=0; y MIPMap::fromBitmap(Bitmap *bitmap) { 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= m_levelWidth[level] || y >= m_levelHeight[level]) return Spectrum(0.0f); break; } return m_pyramid[level][x + m_levelWidth[level]*y]; } Spectrum MIPMap::triangle(int level, Float x, Float y) const { level = clamp(level, 0, m_levels - 1); x = x * m_levelWidth[level] - 0.5f; y = y * m_levelHeight[level] - 0.5f; int xPos = (int) std::floor(x), yPos = (int) std::floor(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; } static StatsCounter mipmapLookups("Texture", "Mip-map texture lookups"); static StatsCounter ewaLookups("Texture", "EWA texture lookups"); Spectrum MIPMap::getValue(const Intersection &its) const { if (!its.hasUVPartials) return triangle(0, its.uv.x, its.uv.y); if (m_isotropic) { ++mipmapLookups; /* Conservatively estimate a square lookup region */ Float width = 2.0f * std::max( std::max(std::abs(its.dudx), std::abs(its.dudy)), std::max(std::abs(its.dvdx), std::abs(its.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, its.uv.x, its.uv.y); } 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, its.uv.x, its.uv.y) * (1.0f - delta) + triangle(level, its.uv.x, its.uv.y) * delta; } } else { Float dudx = its.dudx, dudy = its.dudy, dvdx = its.dvdx, dvdy = its.dvdy; 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 < Epsilon) minorLength = Epsilon; if (minorLength * m_maxAnisotropy < majorLength) { Float scale = majorLength / (minorLength * m_maxAnisotropy); dvdx *= scale; dvdy *= scale; minorLength *= scale; } Float lod = std::max((Float) 0, m_levels - 1 + log2(minorLength)); int ilod = (int) std::floor(lod); Float d = lod - ilod; return EWA(its.uv.x, its.uv.y, dudx, dudy, dvdx, dvdy, ilod) * (1-d) + EWA(its.uv.x, its.uv.y, dudx, dudy, dvdx, dvdy, ilod+1) * d; } } 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::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