00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #ifndef PBRT_MIPMAP_H
00025 #define PBRT_MIPMAP_H
00026
00027 #include "pbrt.h"
00028 #include "color.h"
00029
00030 typedef enum {
00031 TEXTURE_REPEAT,
00032 TEXTURE_BLACK,
00033 TEXTURE_CLAMP
00034 } ImageWrap;
00035 template <class T> class MIPMap {
00036 public:
00037
00038 MIPMap(int xres, int yres, const T *data, bool doTri = false,
00039 float maxAniso = 8.f, ImageWrap wrapMode = TEXTURE_REPEAT);
00040 ~MIPMap();
00041 T Lookup(float s, float t, float width = 0.f) const;
00042 T Lookup(float s, float t, float ds0, float dt0,
00043 float ds1, float dt1) const;
00044 private:
00045
00046 struct ResampleWeight;
00047 ResampleWeight *resampleWeights(int oldres, int newres) {
00048 Assert(newres >= oldres);
00049 ResampleWeight *wt = new ResampleWeight[newres];
00050 float filterwidth = 2.f;
00051 for (int i = 0; i < newres; ++i) {
00052
00053 float center = (i + .5f) * oldres / newres;
00054 wt[i].firstTexel = Floor2Int((center - filterwidth) + 0.5f);
00055 for (int j = 0; j < 4; ++j) {
00056 float pos = wt[i].firstTexel + j + .5f;
00057 wt[i].weight[j] = Lanczos((pos - center) / filterwidth);
00058 }
00059
00060 float invSumWts = 1.f / (wt[i].weight[0] + wt[i].weight[1] +
00061 wt[i].weight[2] + wt[i].weight[3]);
00062 for (int j = 0; j < 4; ++j)
00063 wt[i].weight[j] *= invSumWts;
00064 }
00065 return wt;
00066 }
00067 float clamp(float v) { return Clamp(v, 0.f, INFINITY); }
00068 Spectrum clamp(const Spectrum &v) { return v.Clamp(0.f, INFINITY); }
00069 const T &texel(int level, int s, int t) const;
00070 T triangle(int level, float s, float t) const;
00071 T EWA(float s, float t, float ds0, float dt0, float ds1, float dt1, int level) const;
00072
00073 bool doTrilinear;
00074 float maxAnisotropy;
00075 ImageWrap wrapMode;
00076 struct ResampleWeight {
00077 int firstTexel;
00078 float weight[4];
00079 };
00080 BlockedArray<T> **pyramid;
00081 int nLevels;
00082 #define WEIGHT_LUT_SIZE 128
00083 static float *weightLut;
00084 };
00085
00086 template <class T>
00087 MIPMap<T>::MIPMap(int sres, int tres,
00088 const T *img, bool doTri,
00089 float maxAniso, ImageWrap wm) {
00090 doTrilinear = doTri;
00091 maxAnisotropy = maxAniso;
00092 wrapMode = wm;
00093 T *resampledImage = NULL;
00094 if (!IsPowerOf2(sres) || !IsPowerOf2(tres)) {
00095
00096 int sPow2 = RoundUpPow2(sres), tPow2 = RoundUpPow2(tres);
00097
00098 ResampleWeight *sWeights = resampleWeights(sres, sPow2);
00099 resampledImage = new T[sPow2 * tPow2];
00100
00101 for (int t = 0; t < tres; ++t) {
00102 for (int s = 0; s < sPow2; ++s) {
00103
00104 resampledImage[t*sPow2+s] = 0.;
00105 for (int j = 0; j < 4; ++j) {
00106 int origS = sWeights[s].firstTexel + j;
00107 if (wrapMode == TEXTURE_REPEAT)
00108 origS = Mod(origS, sres);
00109 else if (wrapMode == TEXTURE_CLAMP)
00110 origS = Clamp(origS, 0, sres-1);
00111 if (origS >= 0 && origS < sres)
00112 resampledImage[t*sPow2+s] += sWeights[s].weight[j] *
00113 img[t*sres + origS];
00114 }
00115 }
00116 }
00117 delete[] sWeights;
00118
00119 ResampleWeight *tWeights = resampleWeights(tres, tPow2);
00120 T *workData = new T[tPow2];
00121 for (int s = 0; s < sPow2; ++s) {
00122 for (int t = 0; t < tPow2; ++t) {
00123 workData[t] = 0.;
00124 for (int j = 0; j < 4; ++j) {
00125 int offset = tWeights[t].firstTexel + j;
00126 if (wrapMode == TEXTURE_REPEAT) offset = Mod(offset, tres);
00127 else if (wrapMode == TEXTURE_CLAMP) offset = Clamp(offset, 0, tres-1);
00128 if (offset >= 0 && offset < tres)
00129 workData[t] += tWeights[t].weight[j] *
00130 resampledImage[offset*sPow2 + s];
00131 }
00132 }
00133 for (int t = 0; t < tPow2; ++t)
00134 resampledImage[t*sPow2 + s] = clamp(workData[t]);
00135 }
00136 delete[] workData;
00137 delete[] tWeights;
00138 img = resampledImage;
00139 sres = sPow2;
00140 tres = tPow2;
00141 }
00142
00143 nLevels = 1 + Log2Int(float(max(sres, tres)));
00144 pyramid = new BlockedArray<T> *[nLevels];
00145
00146 pyramid[0] = new BlockedArray<T>(sres, tres, img);
00147 for (int i = 1; i < nLevels; ++i) {
00148
00149 int sRes = max(1, pyramid[i-1]->uSize()/2);
00150 int tRes = max(1, pyramid[i-1]->vSize()/2);
00151 pyramid[i] = new BlockedArray<T>(sRes, tRes);
00152
00153 for (int t = 0; t < tRes; ++t)
00154 for (int s = 0; s < sRes; ++s)
00155 (*pyramid[i])(s, t) = .25f * (
00156 texel(i-1, 2*s, 2*t) +
00157 texel(i-1, 2*s+1, 2*t) +
00158 texel(i-1, 2*s, 2*t+1) +
00159 texel(i-1, 2*s+1, 2*t+1));
00160 }
00161 if (resampledImage) delete[] resampledImage;
00162
00163 if (!weightLut) {
00164 weightLut = (float *)AllocAligned(WEIGHT_LUT_SIZE *
00165 sizeof(float));
00166 for (int i = 0; i < WEIGHT_LUT_SIZE; ++i) {
00167 float alpha = 2;
00168 float r2 = float(i) / float(WEIGHT_LUT_SIZE - 1);
00169 weightLut[i] = expf(-alpha * r2) - expf(-alpha);
00170 }
00171 }
00172 }
00173 template <class T>
00174 const T &MIPMap<T>::texel(int level, int s, int t) const {
00175 const BlockedArray<T> &l = *pyramid[level];
00176
00177 switch (wrapMode) {
00178 case TEXTURE_REPEAT:
00179 s = Mod(s, l.uSize());
00180 t = Mod(t, l.vSize());
00181 break;
00182 case TEXTURE_CLAMP:
00183 s = Clamp(s, 0, l.uSize() - 1);
00184 t = Clamp(t, 0, l.vSize() - 1);
00185 break;
00186 case TEXTURE_BLACK: {
00187 static const T black = 0.f;
00188 if (s < 0 || s >= l.uSize() ||
00189 t < 0 || t >= l.vSize())
00190 return black;
00191 break;
00192 }
00193 }
00194 return l(s, t);
00195 }
00196 template <class T>
00197 MIPMap<T>::~MIPMap() {
00198 for (int i = 0; i < nLevels; ++i)
00199 delete pyramid[i];
00200 delete[] pyramid;
00201 }
00202 template <class T>
00203 T MIPMap<T>::Lookup(float s, float t, float width) const {
00204 static StatsCounter mipTrilerps("Texture",
00205 "Trilinear MIPMap lookups");
00206 ++mipTrilerps;
00207
00208 float level = nLevels - 1 + Log2(max(width, 1e-8f));
00209
00210 if (level < 0)
00211 return triangle(0, s, t);
00212 else if (level >= nLevels - 1)
00213 return texel(nLevels-1, 0, 0);
00214 else {
00215 int iLevel = Floor2Int(level);
00216 float delta = level - iLevel;
00217 return (1.f-delta) * triangle(iLevel, s, t) +
00218 delta * triangle(iLevel+1, s, t);
00219 }
00220 }
00221 template <class T>
00222 T MIPMap<T>::triangle(int level, float s, float t) const {
00223 level = Clamp(level, 0, nLevels-1);
00224 s = s * pyramid[level]->uSize() - 0.5f;
00225 t = t * pyramid[level]->vSize() - 0.5f;
00226 int s0 = Floor2Int(s), t0 = Floor2Int(t);
00227 float ds = s - s0, dt = t - t0;
00228 return (1.f-ds)*(1.f-dt) * texel(level, s0, t0) +
00229 (1.f-ds)*dt * texel(level, s0, t0+1) +
00230 ds*(1.f-dt) * texel(level, s0+1, t0) +
00231 ds*dt * texel(level, s0+1, t0+1);
00232 }
00233 template <class T>
00234 T MIPMap<T>::Lookup(float s, float t, float ds0, float dt0,
00235 float ds1, float dt1) const {
00236 static StatsCounter ewaLookups("Texture", "EWA filter lookups");
00237 ++ewaLookups;
00238 if (doTrilinear)
00239 return Lookup(s, t,
00240 2.f * max(max(fabsf(ds0),
00241 fabsf(dt0)),
00242 max(fabsf(ds1),
00243 fabsf(dt1))));
00244
00245 if (ds0*ds0 + dt0*dt0 < ds1*ds1 + dt1*dt1) {
00246 swap(ds0, ds1);
00247 swap(dt0, dt1);
00248 }
00249 float majorLength = sqrtf(ds0*ds0 + dt0*dt0);
00250 float minorLength = sqrtf(ds1*ds1 + dt1*dt1);
00251
00252 if (minorLength * maxAnisotropy < majorLength) {
00253 float scale = majorLength /
00254 (minorLength * maxAnisotropy);
00255 ds1 *= scale;
00256 dt1 *= scale;
00257 minorLength *= scale;
00258 }
00259
00260 float lod = max(0.f, nLevels - 1.f + Log2(minorLength));
00261 int ilod = Floor2Int(lod);
00262 float d = lod - ilod;
00263 return (1.f - d) * EWA(s, t, ds0, dt0, ds1, dt1, ilod) +
00264 d * EWA(s, t, ds0, dt0, ds1, dt1, ilod+1);
00265 }
00266 template <class T>
00267 T MIPMap<T>::EWA(float s, float t, float ds0, float dt0,
00268 float ds1, float dt1, int level) const {
00269 if (level >= nLevels) return texel(nLevels-1, 0, 0);
00270
00271 s = s * pyramid[level]->uSize() - 0.5f;
00272 t = t * pyramid[level]->vSize() - 0.5f;
00273 ds0 *= pyramid[level]->uSize();
00274 dt0 *= pyramid[level]->vSize();
00275 ds1 *= pyramid[level]->uSize();
00276 dt1 *= pyramid[level]->vSize();
00277
00278 float A = dt0*dt0 + dt1*dt1 + 1;
00279 float B = -2.f * (ds0*dt0 + ds1*dt1);
00280 float C = ds0*ds0 + ds1*ds1 + 1;
00281 float invF = 1.f / (A*C - B*B*0.25f);
00282 A *= invF;
00283 B *= invF;
00284 C *= invF;
00285
00286 float det = -B*B + 4.f*A*C;
00287 float invDet = 1.f / det;
00288 float uSqrt = sqrtf(det * C), vSqrt = sqrtf(A * det);
00289 int s0 = Ceil2Int (s - 2.f * invDet * uSqrt);
00290 int s1 = Floor2Int(s + 2.f * invDet * uSqrt);
00291 int t0 = Ceil2Int (t - 2.f * invDet * vSqrt);
00292 int t1 = Floor2Int(t + 2.f * invDet * vSqrt);
00293 static StatsRatio ewaTexels("Texture", "Texels per EWA lookup");
00294 ewaTexels.Add((1+s1-s0) * (1+t1-t0), 1);
00295
00296 T num(0.);
00297 float den = 0;
00298 for (int it = t0; it <= t1; ++it) {
00299 float tt = it - t;
00300 for (int is = s0; is <= s1; ++is) {
00301 float ss = is - s;
00302
00303 float r2 = A*ss*ss + B*ss*tt + C*tt*tt;
00304 if (r2 < 1.) {
00305 float weight =
00306 weightLut[min(Float2Int(r2 * WEIGHT_LUT_SIZE),
00307 WEIGHT_LUT_SIZE-1)];
00308 num += texel(level, is, it) * weight;
00309 den += weight;
00310 }
00311 }
00312 }
00313 return num / den;
00314 }
00315 template <class T> float *MIPMap<T>::weightLut = NULL;
00316 #endif // PBRT_MIPMAP_H