New improved wavelength sampling scheme.
Jeanphi
1 /***************************************************************************
2 * Copyright (C) 1998-2009 by authors (see AUTHORS.txt ) *
4 * This file is part of LuxRender. *
6 * Lux Renderer is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 3 of the License, or *
9 * (at your option) any later version. *
11 * Lux Renderer is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
19 * This project is based on PBRT ; see http://www.pbrt.org *
20 * Lux Renderer website : http://www.luxrender.net *
21 ***************************************************************************/
23 // initial metropolis light transport sample integrator
26 // TODO: add scaling of output image samples
36 #define SAMPLE_FLOATS 6
38 // mutate a value in the range [0-1]
39 static float mutate(const float x, const float randomValue)
41 static const float s1 = 1.f / 1024.f, s2 = 1.f / 16.f;
42 float dx = s2 * powf(s1 / s2, fabsf(2.f * randomValue - 1.f));
43 if (randomValue < 0.5f) {
45 return (x1 > 1.f) ? x1 - 1.f : x1;
48 return (x1 < 0.f) ? x1 + 1.f : x1;
52 // mutate a value in the range [min-max]
53 static float mutateScaled(const float x, const float randomValue, const float mini, const float maxi, const float range)
55 float dx = range * exp(-log(2.f * range) * fabsf(2.f * randomValue - 1.f));
56 if (randomValue < 0.5f) {
58 return (x1 > maxi) ? x1 - maxi + mini : x1;
61 return (x1 < mini) ? x1 - mini + maxi : x1;
65 // Metropolis method definitions
66 ERPTSampler::ERPTSampler(int totMutations, float microProb, float rng,
68 Sampler(sampler->xPixelStart, sampler->xPixelEnd,
69 sampler->yPixelStart, sampler->yPixelEnd, sampler->samplesPerPixel),
70 normalSamples(0), totalSamples(0), totalTimes(0), totalMutations(totMutations),
71 pMicro(microProb), range(rng), baseSampler(sampler),
72 baseImage(NULL), sampleImage(NULL), currentImage(NULL),
73 baseTimeImage(NULL), timeImage(NULL), currentTimeImage(NULL), offset(NULL),
74 numChains(0), chain(0), mutation(-1), stamp(0), numMicro(-1), posMicro(-1),
75 baseLY(0.f), quantum(0.f), weight(0.f), LY(0.f), alpha(0.f),
76 totalLY(0.), sampleCount(0.)
80 ERPTSampler::~ERPTSampler() {
81 FreeAligned(sampleImage);
82 FreeAligned(baseImage);
83 FreeAligned(timeImage);
84 FreeAligned(baseTimeImage);
89 ERPTSampler* ERPTSampler::clone() const
91 ERPTSampler *newSampler = new ERPTSampler(*this);
92 newSampler->baseSampler = baseSampler->clone();
93 newSampler->totalSamples = 0;
94 newSampler->sampleImage = NULL;
98 static void initERPT(ERPTSampler *sampler, const Sample *sample)
101 sampler->normalSamples = SAMPLE_FLOATS;
102 for (i = 0; i < sample->n1D.size(); ++i)
103 sampler->normalSamples += sample->n1D[i];
104 for (i = 0; i < sample->n2D.size(); ++i)
105 sampler->normalSamples += 2 * sample->n2D[i];
106 sampler->totalSamples = sampler->normalSamples;
107 sampler->offset = new int[sample->nxD.size()];
108 sampler->totalTimes = 0;
109 for (i = 0; i < sample->nxD.size(); ++i) {
110 sampler->offset[i] = sampler->totalSamples;
111 sampler->totalTimes += sample->nxD[i];
112 sampler->totalSamples += sample->dxD[i] * sample->nxD[i];
114 sampler->sampleImage = AllocAligned<float>(sampler->totalSamples);
115 sampler->baseImage = AllocAligned<float>(sampler->totalSamples);
116 sampler->timeImage = AllocAligned<int>(sampler->totalTimes);
117 sampler->baseTimeImage = AllocAligned<int>(sampler->totalTimes);
118 sampler->baseSampler->SetTsPack(sampler->tspack);
119 sampler->baseSampler->SetFilm(sampler->film);
120 sampler->mutation = -1;
122 // Fetch first contribution buffer from pool
123 sampler->contribBuffer = sampler->film->scene->contribPool->Next(NULL);
126 // interface for new ray/samples from scene
127 bool ERPTSampler::GetNextSample(Sample *sample, u_int *use_pos)
129 sample->sampler = this;
130 if (sampleImage == NULL) {
131 initERPT(this, sample);
134 if (mutation == -1) {
135 // Dade - we are at a valid checkpoint where we can stop the
136 // rendering. Check if we have enough samples per pixel in the film.
137 if (film->enoughSamplePerPixel)
140 const bool ret = baseSampler->GetNextSample(sample, use_pos);
141 sample->sampler = this;
142 for (int i = 0; i < totalTimes; ++i)
143 sample->timexD[0][i] = -1;
145 currentImage = baseImage;
146 currentTimeImage = baseTimeImage;
153 for (int i = 0; i < totalTimes; ++i)
154 sample->timexD[0][i] = baseTimeImage[i];
156 currentImage = baseImage;
157 currentTimeImage = baseTimeImage;
160 // *** small mutation ***
161 // mutate current sample
162 float mutationSelector = tspack->rng->floatValue();
163 if (mutationSelector < pMicro)
164 numMicro = min<int>(sample->nxD.size(), Float2Int(mutationSelector / pMicro * (sample->nxD.size() + 1)));
169 for(; maxPos < sample->nxD[numMicro - 1]; ++maxPos)
170 if (sample->timexD[numMicro - 1][maxPos] < sample->stamp)
172 posMicro = min<int>(sample->nxD[numMicro - 1] - 1, Float2Int(tspack->rng->floatValue() * maxPos));
175 sample->imageX = mutateScaled(currentImage[0], tspack->rng->floatValue(), xPixelStart, xPixelEnd, range);
176 sample->imageY = mutateScaled(currentImage[1], tspack->rng->floatValue(), yPixelStart, yPixelEnd, range);
177 sample->lensU = mutate(currentImage[2], tspack->rng->floatValue());
178 sample->lensV = mutate(currentImage[3], tspack->rng->floatValue());
179 sample->time = mutate(currentImage[4], tspack->rng->floatValue());
180 sample->wavelengths = mutate(currentImage[5], tspack->rng->floatValue());
181 for (int i = SAMPLE_FLOATS; i < normalSamples; ++i)
182 sample->oneD[0][i - SAMPLE_FLOATS] = mutate(currentImage[i], tspack->rng->floatValue());
190 float *ERPTSampler::GetLazyValues(Sample *sample, u_int num, u_int pos)
192 const u_int size = sample->dxD[num];
193 float *data = sample->xD[num] + pos * size;
194 int stampLimit = sample->stamp;
195 if (numMicro >= 0 && num != u_int(numMicro - 1) && pos != u_int(posMicro))
197 if (sample->timexD[num][pos] != stampLimit) {
198 if (sample->timexD[num][pos] == -1) {
199 baseSampler->GetLazyValues(sample, num, pos);
200 sample->timexD[num][pos] = 0;
202 int start = offset[num] + pos * size;
203 float *image = currentImage + start;
204 for (u_int i = 0; i < size; ++i)
207 for (int &time = sample->timexD[num][pos]; time < stampLimit; ++time) {
208 for (u_int i = 0; i < size; ++i)
209 data[i] = mutate(data[i], tspack->rng->floatValue());
215 // interface for adding/accepting a new image sample.
216 void ERPTSampler::AddSample(const Sample &sample)
218 vector<Contribution> &newContributions(sample.contributions);
220 for(u_int i = 0; i < newContributions.size(); ++i)
221 newLY += newContributions[i].color.Y();
224 // Add accumulated contribution of previous reference sample
225 weight *= quantum / LY;
226 if (!isinf(weight) && LY > 0.f) {
227 for(u_int i = 0; i < oldContributions.size(); ++i) {
228 // Radiance - added new use of contributionpool/buffers
229 if(&oldContributions && !contribBuffer->Add(&oldContributions[i], weight)) {
230 contribBuffer = film->scene->contribPool->Next(contribBuffer);
231 contribBuffer->Add(&oldContributions[i], weight);
237 if (mutation == -1) {
238 contribBuffer->AddSampleCount(1.f);
240 if (!(newLY > 0.f)) {
241 newContributions.clear();
245 const float meanIntensity = totalLY > 0.f ? totalLY / sampleCount : 1.f;
246 // calculate the number of chains on a new seed
247 quantum = newLY / meanIntensity;
248 numChains = max(1, Floor2Int(quantum + .5f));
249 if (numChains > 100) {
250 printf("%d chains -> %d\n", numChains, totalMutations);
251 numChains = totalMutations;
253 quantum /= (numChains * totalSamples);
255 baseContributions = newContributions;
256 baseImage[0] = sample.imageX;
257 baseImage[1] = sample.imageY;
258 baseImage[2] = sample.lensU;
259 baseImage[3] = sample.lensV;
260 baseImage[4] = sample.time;
261 baseImage[5] = sample.wavelengths;
262 for (int i = SAMPLE_FLOATS; i < totalSamples; ++i)
263 baseImage[i] = sample.oneD[0][i - SAMPLE_FLOATS];
264 for (int i = 0 ; i < totalTimes; ++i)
265 baseTimeImage[i] = sample.timexD[0][i];
267 newContributions.clear();
271 oldContributions = baseContributions;
273 // calculate accept probability from old and new image sample
276 accProb = min(1.f, newLY / LY);
279 float newWeight = accProb;
280 weight += 1.f - accProb;
282 // try accepting of the new sample
283 if (accProb == 1.f || tspack->rng->floatValue() < accProb) {
284 // Add accumulated contribution of previous reference sample
285 weight *= quantum / LY;
286 if (!isinf(weight) && LY > 0.f) {
287 for(u_int i = 0; i < oldContributions.size(); ++i) {
288 // Radiance - added new use of contributionpool/buffers
289 if(&oldContributions && !contribBuffer->Add(&oldContributions[i], weight)) {
290 contribBuffer = film->scene->contribPool->Next(contribBuffer);
291 contribBuffer->Add(&oldContributions[i], weight);
297 oldContributions = newContributions;
299 // Save new contributions for reference
300 sampleImage[0] = sample.imageX;
301 sampleImage[1] = sample.imageY;
302 sampleImage[2] = sample.lensU;
303 sampleImage[3] = sample.lensV;
304 sampleImage[4] = sample.time;
305 sampleImage[5] = sample.wavelengths;
306 for (int i = SAMPLE_FLOATS; i < totalSamples; ++i)
307 sampleImage[i] = sample.oneD[0][i - SAMPLE_FLOATS];
308 for (int i = 0 ; i < totalTimes; ++i)
309 timeImage[i] = sample.timexD[0][i];
310 stamp = sample.stamp;
311 currentImage = sampleImage;
312 currentTimeImage = timeImage;
314 // Add contribution of new sample before rejecting it
315 newWeight *= quantum / newLY;
316 if (!isinf(newWeight) && newLY > 0.f) {
317 for(u_int i = 0; i < newContributions.size(); ++i) {
318 // Radiance - added new use of contributionpool/buffers
319 if(!contribBuffer->Add(&newContributions[i], newWeight)) {
320 contribBuffer = film->scene->contribPool->Next(contribBuffer);
321 contribBuffer->Add(&newContributions[i], newWeight);
326 // Restart from previous reference
327 for (int i = 0; i < totalTimes; ++i)
328 sample.timexD[0][i] = currentTimeImage[i];
329 sample.stamp = stamp;
331 if (++mutation >= totalMutations) {
333 if (++chain >= numChains) {
338 newContributions.clear();
341 Sampler* ERPTSampler::CreateSampler(const ParamSet ¶ms, const Film *film)
343 int xStart, xEnd, yStart, yEnd;
344 film->GetSampleExtent(&xStart, &xEnd, &yStart, &yEnd);
345 int totMutations = params.FindOneInt("chainlength", 100); // number of mutations from a given seed
346 float microMutationProb = params.FindOneFloat("micromutationprob", .5f); //probability of generating a micro sample mutation
347 float range = params.FindOneFloat("mutationrange", (xEnd - xStart + yEnd - yStart) / 50.); // maximum distance in pixel for a small mutation
348 string base = params.FindOneString("basesampler", "random"); // sampler for new chain seed
349 Sampler *sampler = MakeSampler(base, params, film);
350 if (sampler == NULL) {
351 luxError(LUX_SYSTEM, LUX_SEVERE, "ERPTSampler: Could not obtain a valid sampler");
354 return new ERPTSampler(totMutations, microMutationProb, range, sampler);
357 static DynamicLoader::RegisterSampler<ERPTSampler> r("erpt");