00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 #include "pbrt.h"
00026 #include "light.h"
00027 #include "texture.h"
00028 #include "shape.h"
00029 #include "scene.h"
00030 #include "mipmap.h"
00031
00032 struct Distribution1D {
00033
00034 Distribution1D(float *f, int n) {
00035 func = new float[n];
00036 cdf = new float[n+1];
00037 count = n;
00038 memcpy(func, f, n*sizeof(float));
00039 ComputeStep1dCDF(func, n, &funcInt, cdf);
00040 invFuncInt = 1.f / funcInt;
00041 invCount = 1.f / count;
00042 }
00043 float Sample(float u, float *pdf) {
00044
00045 float *ptr = std::lower_bound(cdf, cdf+count+1, u);
00046 int offset = (int) (ptr-cdf-1);
00047
00048 u = (u - cdf[offset]) / (cdf[offset+1] - cdf[offset]);
00049 *pdf = func[offset] * invFuncInt;
00050 return offset + u;
00051 }
00052
00053 float *func, *cdf;
00054 float funcInt, invFuncInt, invCount;
00055 int count;
00056 };
00057
00058 class InfiniteAreaLightIS : public Light {
00059 public:
00060
00061 InfiniteAreaLightIS(const Transform &light2world, const Spectrum &power, int ns,
00062 const string &texmap);
00063 ~InfiniteAreaLightIS();
00064 Spectrum Power(const Scene *scene) const {
00065 Point worldCenter;
00066 float worldRadius;
00067 scene->WorldBound().BoundingSphere(&worldCenter,
00068 &worldRadius);
00069 return Lbase * radianceMap->Lookup(.5f, .5f, .5f) *
00070 M_PI * worldRadius * worldRadius;
00071 }
00072 bool IsDeltaLight() const { return false; }
00073 Spectrum Le(const RayDifferential &r) const;
00074 Spectrum Sample_L(const Point &p, float u1, float u2, Vector *wi, float *pdf,
00075 VisibilityTester *visibility) const;
00076 Spectrum Sample_L(const Scene *scene, float u1, float u2,
00077 float u3, float u4, Ray *ray, float *pdf) const;
00078 float Pdf(const Point &, const Vector &) const;
00079 Spectrum Sample_L(const Point &P, Vector *w, VisibilityTester *visibility) const;
00080
00081 private:
00082
00083 Spectrum Lbase;
00084 MIPMap<Spectrum> *radianceMap;
00085 Distribution1D *uDistrib, **vDistribs;
00086 };
00087
00088
00089 InfiniteAreaLightIS::~InfiniteAreaLightIS() {
00090 delete radianceMap;
00091 }
00092 InfiniteAreaLightIS
00093 ::InfiniteAreaLightIS(const Transform &light2world,
00094 const Spectrum &L, int ns,
00095 const string &texmap)
00096 : Light(light2world, ns)
00097 {
00098 radianceMap = NULL;
00099 if (texmap != "") {
00100 int width, height;
00101 Spectrum *texels =
00102 ReadImage(texmap, &width, &height);
00103 if (texels)
00104 radianceMap =
00105 new MIPMap<Spectrum>(width, height, texels);
00106
00107 float filter = 1.f / max(width, height);
00108 int nu = width, nv = height;
00109 float *img = new float[width*height];
00110 for (int x = 0; x < nu; ++x) {
00111 float xp = (float)x / (float)nu;
00112 for (int y = 0; y < nv; ++y) {
00113 float yp = (float)y / (float)nv;
00114 img[y+x*nv] = radianceMap->Lookup(xp, yp, filter).y();
00115 }
00116 }
00117
00118 float *func = (float *)alloca(max(nu, nv) * sizeof(float));
00119 float *sinVals = (float *)alloca(nv * sizeof(float));
00120 for (int i = 0; i < nv; ++i)
00121 sinVals[i] = sin(M_PI * float(i+.5)/float(nv));
00122 vDistribs = new Distribution1D *[nu];
00123 for (int u = 0; u < nu; ++u) {
00124
00125 for (int v = 0; v < nv; ++v)
00126 func[v] = img[u*nv+v] *= sinVals[v];
00127 vDistribs[u] = new Distribution1D(func, nv);
00128 }
00129
00130 for (int u = 0; u < nu; ++u)
00131 func[u] = vDistribs[u]->funcInt;
00132 uDistrib = new Distribution1D(func, nu);
00133 delete[] img;
00134 delete[] texels;
00135 }
00136 Lbase = L;
00137 }
00138 Spectrum
00139 InfiniteAreaLightIS::Le(const RayDifferential &r) const {
00140 Vector w = r.d;
00141
00142 Spectrum L = Lbase;
00143 if (radianceMap != NULL) {
00144 Vector wh = Normalize(WorldToLight(w));
00145 float s = SphericalPhi(wh) * INV_TWOPI;
00146 float t = SphericalTheta(wh) * INV_PI;
00147 L *= radianceMap->Lookup(s, t);
00148 }
00149 return L;
00150 }
00151 Spectrum InfiniteAreaLightIS::Sample_L(const Point &p, float u1,
00152 float u2, Vector *wi, float *pdf,
00153 VisibilityTester *visibility) const {
00154
00155 float pdfs[2];
00156 float fu = uDistrib->Sample(u1, &pdfs[0]);
00157 int u = Clamp(Float2Int(fu), 0, uDistrib->count-1);
00158 float fv = vDistribs[u]->Sample(u2, &pdfs[1]);
00159
00160 float theta = fv * vDistribs[u]->invCount * M_PI;
00161 float phi = fu * uDistrib->invCount * 2.f * M_PI;
00162 float costheta = cos(theta), sintheta = sin(theta);
00163 float sinphi = sin(phi), cosphi = cos(phi);
00164 *wi = LightToWorld(Vector(sintheta * cosphi, sintheta * sinphi,
00165 costheta));
00166
00167 *pdf = (pdfs[0] * pdfs[1]) / (2. * M_PI * M_PI * sintheta);
00168
00169 visibility->SetRay(p, *wi);
00170 return Lbase * radianceMap->Lookup(fu * uDistrib->invCount,
00171 fv * vDistribs[u]->invCount);
00172 }
00173 float InfiniteAreaLightIS::Pdf(const Point &,
00174 const Vector &w) const {
00175 Vector wi = WorldToLight(w);
00176 float theta = SphericalTheta(wi), phi = SphericalPhi(wi);
00177 int u = Clamp(Float2Int(phi * INV_TWOPI * uDistrib->count),
00178 0, uDistrib->count-1);
00179 int v = Clamp(Float2Int(theta * INV_PI * vDistribs[u]->count),
00180 0, vDistribs[u]->count-1);
00181 return (uDistrib->func[u] * vDistribs[u]->func[v]) /
00182 (uDistrib->funcInt * vDistribs[u]->funcInt) *
00183 1.f / (2.f * M_PI * M_PI * sin(theta));
00184 }
00185 Spectrum InfiniteAreaLightIS::Sample_L(const Scene *scene,
00186 float u1, float u2, float u3, float u4,
00187 Ray *ray, float *pdf) const {
00188
00189 Point worldCenter;
00190 float worldRadius;
00191 scene->WorldBound().BoundingSphere(&worldCenter,
00192 &worldRadius);
00193 worldRadius *= 1.01f;
00194 Point p1 = worldCenter + worldRadius *
00195 UniformSampleSphere(u1, u2);
00196 Point p2 = worldCenter + worldRadius *
00197 UniformSampleSphere(u3, u4);
00198
00199 ray->o = p1;
00200 ray->d = Normalize(p2-p1);
00201
00202 Vector to_center = Normalize(worldCenter - p1);
00203 float costheta = AbsDot(to_center,ray->d);
00204 *pdf =
00205 costheta / ((4.f * M_PI * worldRadius * worldRadius));
00206 return Le(RayDifferential(ray->o, -ray->d));
00207 }
00208 Spectrum InfiniteAreaLightIS::Sample_L(const Point &p,
00209 Vector *wi, VisibilityTester *visibility) const {
00210 float pdf;
00211 Spectrum L = Sample_L(p, RandomFloat(), RandomFloat(),
00212 wi, &pdf, visibility);
00213 if (pdf == 0.) return Spectrum(0.);
00214 return L / pdf;
00215 }
00216
00217 extern "C" DLLEXPORT Light *CreateLight(const Transform &light2world,
00218 const ParamSet ¶mSet) {
00219 Spectrum L = paramSet.FindOneSpectrum("L", Spectrum(1.0));
00220 string texmap = paramSet.FindOneString("mapname", "");
00221 int nSamples = paramSet.FindOneInt("nsamples", 1);
00222
00223 return new InfiniteAreaLightIS(light2world, L, nSamples, texmap);
00224 }