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
00026 #include "stdafx.h"
00027 #include "lights/infinite.h"
00028 #include "sh.h"
00029 #include "montecarlo.h"
00030 #include "paramset.h"
00031 #include "imageio.h"
00032
00033
00034 struct InfiniteAreaCube {
00035
00036 InfiniteAreaCube(const InfiniteAreaLight *l, const Scene *s,
00037 float t, bool cv, float pe)
00038 : light(l), scene(s), time(t), pEpsilon(pe), computeVis(cv) { }
00039 Spectrum operator()(int, int, const Point &p, const Vector &w) {
00040 Ray ray(p, w, pEpsilon, INFINITY, time);
00041 if (!computeVis || !scene->IntersectP(ray))
00042 return light->Le(RayDifferential(ray));
00043 return 0.f;
00044 }
00045 const InfiniteAreaLight *light;
00046 const Scene *scene;
00047 float time, pEpsilon;
00048 bool computeVis;
00049 };
00050
00051
00052
00053
00054 InfiniteAreaLight::~InfiniteAreaLight() {
00055 delete distribution;
00056 delete radianceMap;
00057 }
00058
00059
00060 InfiniteAreaLight::InfiniteAreaLight(const Transform &light2world,
00061 const Spectrum &L, int ns, const string &texmap)
00062 : Light(light2world, ns) {
00063 int width = 0, height = 0;
00064 RGBSpectrum *texels = NULL;
00065
00066 if (texmap != "") {
00067 texels = ReadImage(texmap, &width, &height);
00068 if (texels)
00069 for (int i = 0; i < width * height; ++i)
00070 texels[i] *= L.ToRGBSpectrum();
00071 }
00072 if (!texels) {
00073 width = height = 1;
00074 texels = new RGBSpectrum[1];
00075 texels[0] = L.ToRGBSpectrum();
00076 }
00077 radianceMap = new MIPMap<RGBSpectrum>(width, height, texels);
00078 delete[] texels;
00079
00080
00081
00082 float filter = 1.f / max(width, height);
00083 float *img = new float[width*height];
00084 for (int v = 0; v < height; ++v) {
00085 float vp = (float)v / (float)height;
00086 float sinTheta = sinf(M_PI * float(v+.5f)/float(height));
00087 for (int u = 0; u < width; ++u) {
00088 float up = (float)u / (float)width;
00089 img[u+v*width] = radianceMap->Lookup(up, vp, filter).y();
00090 img[u+v*width] *= sinTheta;
00091 }
00092 }
00093
00094
00095 distribution = new Distribution2D(img, width, height);
00096 delete[] img;
00097 }
00098
00099
00100 Spectrum InfiniteAreaLight::Power(const Scene *scene) const {
00101 Point worldCenter;
00102 float worldRadius;
00103 scene->WorldBound().BoundingSphere(&worldCenter, &worldRadius);
00104 return M_PI * worldRadius * worldRadius *
00105 Spectrum(radianceMap->Lookup(.5f, .5f, .5f), SPECTRUM_ILLUMINANT);
00106 }
00107
00108
00109 Spectrum InfiniteAreaLight::Le(const RayDifferential &r) const {
00110 Vector wh = Normalize(WorldToLight(r.d));
00111 float s = SphericalPhi(wh) * INV_TWOPI;
00112 float t = SphericalTheta(wh) * INV_PI;
00113 return Spectrum(radianceMap->Lookup(s, t), SPECTRUM_ILLUMINANT);
00114 }
00115
00116
00117 void InfiniteAreaLight::SHProject(const Point &p, float pEpsilon,
00118 int lmax, const Scene *scene, bool computeLightVis,
00119 float time, RNG &rng, Spectrum *coeffs) const {
00120
00121 if (computeLightVis) {
00122 Light::SHProject(p, pEpsilon, lmax, scene, computeLightVis,
00123 time, rng, coeffs);
00124 return;
00125 }
00126 for (int i = 0; i < SHTerms(lmax); ++i)
00127 coeffs[i] = 0.f;
00128 int ntheta = radianceMap->Height(), nphi = radianceMap->Width();
00129 if (min(ntheta, nphi) > 50) {
00130
00131
00132
00133 float *buf = new float[2*ntheta + 2*nphi];
00134 float *bufp = buf;
00135 float *sintheta = bufp; bufp += ntheta;
00136 float *costheta = bufp; bufp += ntheta;
00137 float *sinphi = bufp; bufp += nphi;
00138 float *cosphi = bufp;
00139 for (int theta = 0; theta < ntheta; ++theta) {
00140 sintheta[theta] = sinf((theta + .5f)/ntheta * M_PI);
00141 costheta[theta] = cosf((theta + .5f)/ntheta * M_PI);
00142 }
00143 for (int phi = 0; phi < nphi; ++phi) {
00144 sinphi[phi] = sinf((phi + .5f)/nphi * 2.f * M_PI);
00145 cosphi[phi] = cosf((phi + .5f)/nphi * 2.f * M_PI);
00146 }
00147 float *Ylm = ALLOCA(float, SHTerms(lmax));
00148 for (int theta = 0; theta < ntheta; ++theta) {
00149 for (int phi = 0; phi < nphi; ++phi) {
00150
00151 Vector w = Vector(sintheta[theta] * cosphi[phi],
00152 sintheta[theta] * sinphi[phi],
00153 costheta[theta]);
00154 w = Normalize(LightToWorld(w));
00155 Spectrum Le = Spectrum(radianceMap->Texel(0, phi, theta),
00156 SPECTRUM_ILLUMINANT);
00157 SHEvaluate(w, lmax, Ylm);
00158 for (int i = 0; i < SHTerms(lmax); ++i)
00159 coeffs[i] += Le * Ylm[i] * sintheta[theta] *
00160 (M_PI / ntheta) * (2.f * M_PI / nphi);
00161 }
00162 }
00163
00164
00165 delete[] buf;
00166 }
00167 else {
00168
00169 SHProjectCube(InfiniteAreaCube(this, scene, time, computeLightVis,
00170 pEpsilon),
00171 p, 200, lmax, coeffs);
00172 }
00173 }
00174
00175
00176 InfiniteAreaLight *CreateInfiniteLight(const Transform &light2world,
00177 const ParamSet ¶mSet) {
00178 Spectrum L = paramSet.FindOneSpectrum("L", Spectrum(1.0));
00179 Spectrum sc = paramSet.FindOneSpectrum("scale", Spectrum(1.0));
00180 string texmap = paramSet.FindOneString("mapname", "");
00181 int nSamples = paramSet.FindOneInt("nsamples", 1);
00182 if (PbrtOptions.quickRender) nSamples = max(1, nSamples / 4);
00183 return new InfiniteAreaLight(light2world, L * sc, nSamples, texmap);
00184 }
00185
00186
00187 Spectrum InfiniteAreaLight::Sample_L(const Point &p, float pEpsilon,
00188 const LightSample &ls, float time, Vector *wi, float *pdf,
00189 VisibilityTester *visibility) const {
00190 PBRT_INFINITE_LIGHT_STARTED_SAMPLE();
00191
00192 float uv[2], mapPdf;
00193 distribution->SampleContinuous(ls.uPos[0], ls.uPos[1], uv, &mapPdf);
00194 if (mapPdf == 0.f) return 0.f;
00195
00196
00197 float theta = uv[1] * M_PI, phi = uv[0] * 2.f * M_PI;
00198 float costheta = cosf(theta), sintheta = sinf(theta);
00199 float sinphi = sinf(phi), cosphi = cosf(phi);
00200 *wi = LightToWorld(Vector(sintheta * cosphi, sintheta * sinphi,
00201 costheta));
00202
00203
00204 *pdf = mapPdf / (2.f * M_PI * M_PI * sintheta);
00205 if (sintheta == 0.f) *pdf = 0.f;
00206
00207
00208 visibility->SetRay(p, pEpsilon, *wi, time);
00209 Spectrum Ls = Spectrum(radianceMap->Lookup(uv[0], uv[1]),
00210 SPECTRUM_ILLUMINANT);
00211 PBRT_INFINITE_LIGHT_FINISHED_SAMPLE();
00212 return Ls;
00213 }
00214
00215
00216 float InfiniteAreaLight::Pdf(const Point &, const Vector &w) const {
00217 PBRT_INFINITE_LIGHT_STARTED_PDF();
00218 Vector wi = WorldToLight(w);
00219 float theta = SphericalTheta(wi), phi = SphericalPhi(wi);
00220 float sintheta = sinf(theta);
00221 if (sintheta == 0.f) return 0.f;
00222 float p = distribution->Pdf(phi * INV_TWOPI, theta * INV_PI) /
00223 (2.f * M_PI * M_PI * sintheta);
00224 PBRT_INFINITE_LIGHT_FINISHED_PDF();
00225 return p;
00226 }
00227
00228
00229 Spectrum InfiniteAreaLight::Sample_L(const Scene *scene,
00230 const LightSample &ls, float u1, float u2, float time,
00231 Ray *ray, Normal *Ns, float *pdf) const {
00232 PBRT_INFINITE_LIGHT_STARTED_SAMPLE();
00233
00234
00235
00236 float uv[2], mapPdf;
00237 distribution->SampleContinuous(ls.uPos[0], ls.uPos[1], uv, &mapPdf);
00238 if (mapPdf == 0.f) return Spectrum(0.f);
00239
00240 float theta = uv[1] * M_PI, phi = uv[0] * 2.f * M_PI;
00241 float costheta = cosf(theta), sintheta = sinf(theta);
00242 float sinphi = sinf(phi), cosphi = cosf(phi);
00243 Vector d = -LightToWorld(Vector(sintheta * cosphi, sintheta * sinphi,
00244 costheta));
00245 *Ns = (Normal)d;
00246
00247
00248 Point worldCenter;
00249 float worldRadius;
00250 scene->WorldBound().BoundingSphere(&worldCenter, &worldRadius);
00251 Vector v1, v2;
00252 CoordinateSystem(-d, &v1, &v2);
00253 float d1, d2;
00254 ConcentricSampleDisk(u1, u2, &d1, &d2);
00255 Point Pdisk = worldCenter + worldRadius * (d1 * v1 + d2 * v2);
00256 *ray = Ray(Pdisk + worldRadius * -d, d, 0., INFINITY, time);
00257
00258
00259 float directionPdf = mapPdf / (2.f * M_PI * M_PI * sintheta);
00260 float areaPdf = 1.f / (M_PI * worldRadius * worldRadius);
00261 *pdf = directionPdf * areaPdf;
00262 if (sintheta == 0.f) *pdf = 0.f;
00263 Spectrum Ls = (radianceMap->Lookup(uv[0], uv[1]), SPECTRUM_ILLUMINANT);
00264 PBRT_INFINITE_LIGHT_FINISHED_SAMPLE();
00265 return Ls;
00266 }
00267
00268