samplers/erpt.cpp
author Jean-Philippe Grimaldi <jeanphi@via.ecp.fr>
Thu Oct 15 00:38:46 2009 +0200 (2009-10-15)
changeset 1643 eac52d5c6a9e
parent 1624 bacd16526b46
child 1661 e32dd0c4e86d
permissions -rwxr-xr-x
New improved wavelength sampling scheme.

Jeanphi
     1 /***************************************************************************
     2  *   Copyright (C) 1998-2009 by authors (see AUTHORS.txt )                 *
     3  *                                                                         *
     4  *   This file is part of LuxRender.                                       *
     5  *                                                                         *
     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.                                   *
    10  *                                                                         *
    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.                          *
    15  *                                                                         *
    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/>. *
    18  *                                                                         *
    19  *   This project is based on PBRT ; see http://www.pbrt.org               *
    20  *   Lux Renderer website : http://www.luxrender.net                       *
    21  ***************************************************************************/
    22 
    23 // initial metropolis light transport sample integrator
    24 // by radiance
    25 
    26 // TODO: add scaling of output image samples
    27 
    28 // erpt.cpp*
    29 #include "erpt.h"
    30 #include "dynload.h"
    31 #include "scene.h"
    32 #include "error.h"
    33 
    34 using namespace lux;
    35 
    36 #define SAMPLE_FLOATS 6
    37 
    38 // mutate a value in the range [0-1]
    39 static float mutate(const float x, const float randomValue)
    40 {
    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) {
    44 		float x1 = x + dx;
    45 		return (x1 > 1.f) ? x1 - 1.f : x1;
    46 	} else {
    47 		float x1 = x - dx;
    48 		return (x1 < 0.f) ? x1 + 1.f : x1;
    49 	}
    50 }
    51 
    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)
    54 {
    55 	float dx = range * exp(-log(2.f * range) * fabsf(2.f * randomValue - 1.f));
    56 	if (randomValue < 0.5f) {
    57 		float x1 = x + dx;
    58 		return (x1 > maxi) ? x1 - maxi + mini : x1;
    59 	} else {
    60 		float x1 = x - dx;
    61 		return (x1 < mini) ? x1 - mini + maxi : x1;
    62 	}
    63 }
    64 
    65 // Metropolis method definitions
    66 ERPTSampler::ERPTSampler(int totMutations, float microProb, float rng,
    67 	Sampler *sampler) :
    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.)
    77 {
    78 }
    79 
    80 ERPTSampler::~ERPTSampler() {
    81 	FreeAligned(sampleImage);
    82 	FreeAligned(baseImage);
    83 	FreeAligned(timeImage);
    84 	FreeAligned(baseTimeImage);
    85 	delete baseSampler;
    86 }
    87 
    88 // Copy
    89 ERPTSampler* ERPTSampler::clone() const
    90 {
    91 	ERPTSampler *newSampler = new ERPTSampler(*this);
    92 	newSampler->baseSampler = baseSampler->clone();
    93 	newSampler->totalSamples = 0;
    94 	newSampler->sampleImage = NULL;
    95 	return newSampler;
    96 }
    97 
    98 static void initERPT(ERPTSampler *sampler, const Sample *sample)
    99 {
   100 	u_int i;
   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];
   113 	}
   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;
   121 
   122 	// Fetch first contribution buffer from pool
   123 	sampler->contribBuffer = sampler->film->scene->contribPool->Next(NULL);
   124 }
   125 
   126 // interface for new ray/samples from scene
   127 bool ERPTSampler::GetNextSample(Sample *sample, u_int *use_pos)
   128 {
   129 	sample->sampler = this;
   130 	if (sampleImage == NULL) {
   131 		initERPT(this, sample);
   132 	}
   133 
   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)
   138 			return false;
   139 
   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;
   144 		sample->stamp = 0;
   145 		currentImage = baseImage;
   146 		currentTimeImage = baseTimeImage;
   147 		stamp = 0;
   148 		numMicro = -1;
   149 		return ret;
   150 	} else {
   151 		if (mutation == 0) {
   152 			// *** new chain ***
   153 			for (int i = 0; i < totalTimes; ++i)
   154 				sample->timexD[0][i] = baseTimeImage[i];
   155 			sample->stamp = 0;
   156 			currentImage = baseImage;
   157 			currentTimeImage = baseTimeImage;
   158 			stamp = 0;
   159 		}
   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)));
   165 		else
   166 			numMicro = -1;
   167 		if (numMicro > 0) {
   168 			u_int maxPos = 0;
   169 			for(; maxPos < sample->nxD[numMicro - 1]; ++maxPos)
   170 				if (sample->timexD[numMicro - 1][maxPos] < sample->stamp)
   171 					break;
   172 			posMicro = min<int>(sample->nxD[numMicro - 1] - 1, Float2Int(tspack->rng->floatValue() * maxPos));
   173 		} else {
   174 			posMicro = -1;
   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());
   183 		}
   184 		++(sample->stamp);
   185 	}
   186 
   187     return true;
   188 }
   189 
   190 float *ERPTSampler::GetLazyValues(Sample *sample, u_int num, u_int pos)
   191 {
   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))
   196 		--stampLimit;
   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;
   201 		} else {
   202 			int start = offset[num] + pos * size;
   203 			float *image = currentImage + start;
   204 			for (u_int i = 0; i < size; ++i)
   205 				data[i] = image[i];
   206 		}
   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());
   210 		}
   211 	}
   212 	return data;
   213 }
   214 
   215 // interface for adding/accepting a new image sample.
   216 void ERPTSampler::AddSample(const Sample &sample)
   217 {
   218 	vector<Contribution> &newContributions(sample.contributions);
   219 	float newLY = 0.0f;
   220 	for(u_int i = 0; i < newContributions.size(); ++i)
   221 		newLY += newContributions[i].color.Y();
   222 	if (mutation <= 0) {
   223 		if (weight > 0.f) {
   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);
   232 					}
   233 				}
   234 			}
   235 			weight = 0.f;
   236 		}
   237 		if (mutation == -1) {
   238 			contribBuffer->AddSampleCount(1.f);
   239 			++sampleCount;
   240 			if (!(newLY > 0.f)) {
   241 				newContributions.clear();
   242 				return;
   243 			}
   244 			totalLY += newLY;
   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;
   252 			}
   253 			quantum /= (numChains * totalSamples);
   254 			baseLY = newLY;
   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];
   266 			mutation = 0;
   267 			newContributions.clear();
   268 			return;
   269 		}
   270 		LY = baseLY;
   271 		oldContributions = baseContributions;
   272 	}
   273 	// calculate accept probability from old and new image sample
   274 	float accProb;
   275 	if (LY > 0.f)
   276 		accProb = min(1.f, newLY / LY);
   277 	else
   278 		accProb = 1.f;
   279 	float newWeight = accProb;
   280 	weight += 1.f - accProb;
   281 
   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);
   292 				}
   293 			}
   294 		}
   295 		weight = newWeight;
   296 		LY = newLY;
   297 		oldContributions = newContributions;
   298 
   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;
   313 	} else {
   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);
   322 				}
   323 			}
   324 		}
   325 
   326 		// Restart from previous reference
   327 		for (int i = 0; i < totalTimes; ++i)
   328 			sample.timexD[0][i] = currentTimeImage[i];
   329 		sample.stamp = stamp;
   330 	}
   331 	if (++mutation >= totalMutations) {
   332 		mutation = 0;
   333 		if (++chain >= numChains) {
   334 			chain = 0;
   335 			mutation = -1;
   336 		}
   337 	}
   338 	newContributions.clear();
   339 }
   340 
   341 Sampler* ERPTSampler::CreateSampler(const ParamSet &params, const Film *film)
   342 {
   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");
   352 		return NULL;
   353 	}
   354 	return new ERPTSampler(totMutations, microMutationProb, range, sampler);
   355 }
   356 
   357 static DynamicLoader::RegisterSampler<ERPTSampler> r("erpt");