00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include "irradiancecache.h"
00025
00026 using namespace lux;
00027
00028
00029 IrradianceCache* IrradianceCache::clone() const
00030 {
00031 return new IrradianceCache(*this);
00032 }
00033
00034 IrradianceCache::IrradianceCache(int maxspec, int maxind,
00035 float maxerr, int ns) {
00036 maxError = maxerr;
00037 nSamples = ns;
00038 maxSpecularDepth = maxspec;
00039 maxIndirectDepth = maxind;
00040 specularDepth = 0;
00041 }
00042 void IrradianceCache::RequestSamples(Sample *sample,
00043 const Scene *scene) {
00044
00045 u_int nLights = scene->lights.size();
00046 lightSampleOffset = new int[nLights];
00047 bsdfSampleOffset = new int[nLights];
00048 bsdfComponentOffset = new int[nLights];
00049 for (u_int i = 0; i < nLights; ++i) {
00050 const Light *light = scene->lights[i];
00051 int lightSamples =
00052 scene->sampler->RoundSize(light->nSamples);
00053 lightSampleOffset[i] = sample->Add2D(lightSamples);
00054 bsdfSampleOffset[i] = sample->Add2D(lightSamples);
00055 bsdfComponentOffset[i] = sample->Add1D(lightSamples);
00056 }
00057 lightNumOffset = -1;
00058 }
00059 Spectrum IrradianceCache::Li(const Scene *scene, const RayDifferential &ray,
00060 const Sample *sample, float *alpha) const {
00061 Intersection isect;
00062 Spectrum L(0.);
00063 if (scene->Intersect(ray, &isect)) {
00064 if (alpha) *alpha = 1.;
00065
00066 BSDF *bsdf = isect.GetBSDF(ray);
00067 Vector wo = -ray.d;
00068 const Point &p = bsdf->dgShading.p;
00069 const Normal &n = bsdf->dgShading.nn;
00070
00071 L += isect.Le(wo);
00072 L += UniformSampleAllLights(scene, p, n, wo, bsdf, sample,
00073 lightSampleOffset, bsdfSampleOffset,
00074 bsdfComponentOffset);
00075
00076 if (specularDepth++ < maxSpecularDepth) {
00077 Vector wi;
00078
00079 Spectrum f = bsdf->Sample_f(wo, &wi,
00080 BxDFType(BSDF_REFLECTION | BSDF_SPECULAR));
00081 if (!f.Black()) {
00082
00083 RayDifferential rd(p, wi);
00084 rd.hasDifferentials = true;
00085 rd.rx.o = p + isect.dg.dpdx;
00086 rd.ry.o = p + isect.dg.dpdy;
00087
00088 Normal dndx = bsdf->dgShading.dndu * bsdf->dgShading.dudx +
00089 bsdf->dgShading.dndv * bsdf->dgShading.dvdx;
00090 Normal dndy = bsdf->dgShading.dndu * bsdf->dgShading.dudy +
00091 bsdf->dgShading.dndv * bsdf->dgShading.dvdy;
00092 Vector dwodx = -ray.rx.d - wo, dwody = -ray.ry.d - wo;
00093 float dDNdx = Dot(dwodx, n) + Dot(wo, dndx);
00094 float dDNdy = Dot(dwody, n) + Dot(wo, dndy);
00095 rd.rx.d = wi -
00096 dwodx + 2 * Vector(Dot(wo, n) * dndx +
00097 dDNdx * n);
00098 rd.ry.d = wi -
00099 dwody + 2 * Vector(Dot(wo, n) * dndy +
00100 dDNdy * n);
00101 L += scene->Li(rd, sample) * f * AbsDot(wi, n);
00102 }
00103 f = bsdf->Sample_f(wo, &wi,
00104 BxDFType(BSDF_TRANSMISSION | BSDF_SPECULAR));
00105 if (!f.Black()) {
00106
00107 RayDifferential rd(p, wi);
00108 rd.hasDifferentials = true;
00109 rd.rx.o = p + isect.dg.dpdx;
00110 rd.ry.o = p + isect.dg.dpdy;
00111
00112 float eta = bsdf->eta;
00113 Vector w = -wo;
00114 if (Dot(wo, n) < 0) eta = 1.f / eta;
00115
00116 Normal dndx = bsdf->dgShading.dndu * bsdf->dgShading.dudx + bsdf->dgShading.dndv * bsdf->dgShading.dvdx;
00117 Normal dndy = bsdf->dgShading.dndu * bsdf->dgShading.dudy + bsdf->dgShading.dndv * bsdf->dgShading.dvdy;
00118
00119 Vector dwodx = -ray.rx.d - wo, dwody = -ray.ry.d - wo;
00120 float dDNdx = Dot(dwodx, n) + Dot(wo, dndx);
00121 float dDNdy = Dot(dwody, n) + Dot(wo, dndy);
00122
00123 float mu = eta * Dot(w, n) - Dot(wi, n);
00124 float dmudx = (eta - (eta*eta*Dot(w,n))/Dot(wi, n)) * dDNdx;
00125 float dmudy = (eta - (eta*eta*Dot(w,n))/Dot(wi, n)) * dDNdy;
00126
00127 rd.rx.d = wi + eta * dwodx - Vector(mu * dndx + dmudx * n);
00128 rd.ry.d = wi + eta * dwody - Vector(mu * dndy + dmudy * n);
00129 L += scene->Li(rd, sample) * f * AbsDot(wi, n);
00130 }
00131 }
00132 --specularDepth;
00133
00134 Normal ng = isect.dg.nn;
00135 if (Dot(wo, ng) < 0.f) ng = -ng;
00136 BxDFType flags = BxDFType(BSDF_REFLECTION |
00137 BSDF_DIFFUSE |
00138 BSDF_GLOSSY);
00139 L += IndirectLo(p, ng, wo, bsdf, flags, sample, scene);
00140 flags = BxDFType(BSDF_TRANSMISSION |
00141 BSDF_DIFFUSE |
00142 BSDF_GLOSSY);
00143 L += IndirectLo(p, -ng, wo, bsdf, flags, sample, scene);
00144 }
00145 else {
00146
00147 if (alpha) *alpha = 0.;
00148 for (u_int i = 0; i < scene->lights.size(); ++i)
00149 L += scene->lights[i]->Le(ray);
00150 if (alpha && !L.Black()) *alpha = 1.;
00151 return L;
00152 }
00153 return L;
00154 }
00155 Spectrum IrradianceCache::IndirectLo(const Point &p,
00156 const Normal &n, const Vector &wo, BSDF *bsdf,
00157 BxDFType flags, const Sample *sample,
00158 const Scene *scene) const {
00159 if (bsdf->NumComponents(flags) == 0)
00160 return Spectrum(0.);
00161 Spectrum E;
00162 if (!InterpolateIrradiance(scene, p, n, &E)) {
00163
00164 u_int scramble[2] = { lux::random::uintValue(), lux::random::uintValue() };
00165 float sumInvDists = 0.;
00166 for (int i = 0; i < nSamples; ++i) {
00167
00168
00169
00170 float u[2];
00171 Sample02(i, scramble, u);
00172 Vector w = CosineSampleHemisphere(u[0], u[1]);
00173 RayDifferential r(p, bsdf->LocalToWorld(w));
00174 if (Dot(r.d, n) < 0) r.d = -r.d;
00175 Spectrum L(0.);
00176
00177 {
00178
00179 Spectrum pathThroughput = 1.;
00180 RayDifferential ray(r);
00181 bool specularBounce = false;
00182 for (int pathLength = 0; ; ++pathLength) {
00183
00184 Intersection isect;
00185 if (!scene->Intersect(ray, &isect))
00186 break;
00187 if (pathLength == 0)
00188 r.maxt = ray.maxt;
00189 pathThroughput *= scene->Transmittance(ray);
00190
00191 if (specularBounce)
00192 L += pathThroughput * isect.Le(-ray.d);
00193
00194 MemoryArena arena;
00195 BSDF *bsdf = isect.GetBSDF(ray);
00196
00197 const Point &p = bsdf->dgShading.p;
00198 const Normal &n = bsdf->dgShading.nn;
00199 Vector wo = -ray.d;
00200 L += pathThroughput *
00201 UniformSampleOneLight(scene, p, n, wo, bsdf, sample);
00202 if (pathLength+1 == maxIndirectDepth) break;
00203
00204
00205 float bs1 = lux::random::floatValue(), bs2 = lux::random::floatValue(), bcs = lux::random::floatValue();
00206 Vector wi;
00207 float pdf;
00208 BxDFType flags;
00209 Spectrum f = bsdf->Sample_f(wo, &wi, bs1, bs2, bcs,
00210 &pdf, BSDF_ALL, &flags);
00211 if (f.Black() || pdf == 0.)
00212 break;
00213 specularBounce = (flags & BSDF_SPECULAR) != 0;
00214 pathThroughput *= f * AbsDot(wi, n) / pdf;
00215 ray = RayDifferential(p, wi);
00216
00217 if (pathLength > 3) {
00218 float continueProbability = .5f;
00219 if (lux::random::floatValue() > continueProbability)
00220 break;
00221 pathThroughput /= continueProbability;
00222 }
00223 }
00224 }
00225 E += L;
00226 float dist = r.maxt * r.d.Length();
00227 sumInvDists += 1.f / dist;
00228 }
00229 E *= M_PI / float(nSamples);
00230
00231
00232
00233
00234 static float minMaxDist =
00235 .001f * powf(scene->WorldBound().Volume(), 1.f/3.f);
00236 static float maxMaxDist =
00237 .125f * powf(scene->WorldBound().Volume(), 1.f/3.f);
00238 float maxDist = nSamples / sumInvDists;
00239 if (minMaxDist > 0.f)
00240 maxDist = Clamp(maxDist, minMaxDist, maxMaxDist);
00241 maxDist *= maxError;
00242 BBox sampleExtent(p);
00243 sampleExtent.Expand(maxDist);
00244 octree->Add(IrradianceSample(E, p, n, maxDist),
00245 sampleExtent);
00246 }
00247
00248 return INV_PI * bsdf->rho(wo, flags) * E;
00249 }
00250 void IrradianceCache::Preprocess(const Scene *scene) {
00251 BBox wb = scene->WorldBound();
00252 Vector delta = .01f * (wb.pMax - wb.pMin);
00253 wb.pMin -= delta;
00254 wb.pMax += delta;
00255 octree = new Octree<IrradianceSample, IrradProcess>(wb);
00256 }
00257 IrradianceCache::~IrradianceCache() {
00258 delete octree;
00259 }
00260 bool
00261 IrradianceCache::InterpolateIrradiance(const Scene *scene,
00262 const Point &p, const Normal &n, Spectrum *E) const {
00263 if (!octree) return false;
00264 IrradProcess proc(n, maxError);
00265 octree->Lookup(p, proc);
00266
00267
00268
00269
00270
00271
00272
00273 if (!proc.Successful()) return false;
00274 *E = proc.GetIrradiance();
00275 return true;
00276 }
00277 void IrradProcess::operator()(const Point &p,
00278 const IrradianceSample &sample) const {
00279 ++samplesChecked;
00280
00281 if (Dot(n, sample.n) < 0.01f)
00282 return;
00283
00284 float d2 = DistanceSquared(p, sample.p);
00285 if (d2 > sample.maxDist * sample.maxDist)
00286 return;
00287
00288 Normal navg = sample.n + n;
00289 if (Dot(p - sample.p, navg) < -.01f)
00290 return;
00291
00292 float err = sqrtf(d2) / (sample.maxDist * Dot(n, sample.n));
00293 if (err < 1.) {
00294 ++nFound;
00295 float wt = (1.f - err) * (1.f - err);
00296 E += wt * sample.E;
00297 sumWt += wt;
00298 }
00299 }
00300 SurfaceIntegrator* IrradianceCache::CreateSurfaceIntegrator(const ParamSet ¶ms) {
00301 float maxError = params.FindOneFloat("maxerror", .2f);
00302 int maxSpecularDepth = params.FindOneInt("maxspeculardepth", 5);
00303 int maxIndirectDepth = params.FindOneInt("maxindirectdepth", 3);
00304 int nSamples = params.FindOneInt("nsamples", 4096);
00305 return new IrradianceCache(maxSpecularDepth, maxIndirectDepth,
00306 maxError, nSamples);
00307 }