samplers/erpt.cpp
author Jean-Philippe Grimaldi <jeanphi@via.ecp.fr>
Tue Apr 20 14:09:18 2010 +0200 (2010-04-20)
changeset 2174 4476a60abb5a
parent 1661 e32dd0c4e86d
child 2380 be303be36f48
permissions -rwxr-xr-x
Fix a nasty typo in ERPT.

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 * expf(-logf(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(u_int totMutations, 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  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(~0U), stamp(0),
    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 u_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 = ~0U;
   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 == ~0U) {
   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 (u_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 		return ret;
   149 	} else {
   150 		if (mutation == 0) {
   151 			// *** new chain ***
   152 			for (u_int i = 0; i < totalTimes; ++i)
   153 				sample->timexD[0][i] = baseTimeImage[i];
   154 			sample->stamp = 0;
   155 			currentImage = baseImage;
   156 			currentTimeImage = baseTimeImage;
   157 			stamp = 0;
   158 		}
   159 		// *** small mutation ***
   160 		// mutate current sample
   161 		sample->imageX = mutateScaled(currentImage[0], tspack->rng->floatValue(), xPixelStart, xPixelEnd, range);
   162 		sample->imageY = mutateScaled(currentImage[1], tspack->rng->floatValue(), yPixelStart, yPixelEnd, range);
   163 		sample->lensU = mutate(currentImage[2], tspack->rng->floatValue());
   164 		sample->lensV = mutate(currentImage[3], tspack->rng->floatValue());
   165 		sample->time = mutate(currentImage[4], tspack->rng->floatValue());
   166 		sample->wavelengths = mutate(currentImage[5], tspack->rng->floatValue());
   167 		for (u_int i = SAMPLE_FLOATS; i < normalSamples; ++i)
   168 				sample->oneD[0][i - SAMPLE_FLOATS] = mutate(currentImage[i], tspack->rng->floatValue());
   169 		++(sample->stamp);
   170 	}
   171 
   172 	return true;
   173 }
   174 
   175 float *ERPTSampler::GetLazyValues(Sample *sample, u_int num, u_int pos)
   176 {
   177 	const u_int size = sample->dxD[num];
   178 	float *data = sample->xD[num] + pos * size;
   179 	const int stampLimit = sample->stamp;
   180 	if (sample->timexD[num][pos] != stampLimit) {
   181 		if (sample->timexD[num][pos] == -1) {
   182 			baseSampler->GetLazyValues(sample, num, pos);
   183 			sample->timexD[num][pos] = 0;
   184 		} else {
   185 			const u_int start = offset[num] + pos * size;
   186 			float *image = currentImage + start;
   187 			for (u_int i = 0; i < size; ++i)
   188 				data[i] = image[i];
   189 		}
   190 		for (int &time = sample->timexD[num][pos]; time < stampLimit; ++time) {
   191 			for (u_int i = 0; i < size; ++i)
   192 				data[i] = mutate(data[i], tspack->rng->floatValue());
   193 		}
   194 	}
   195 	return data;
   196 }
   197 
   198 // interface for adding/accepting a new image sample.
   199 void ERPTSampler::AddSample(const Sample &sample)
   200 {
   201 	vector<Contribution> &newContributions(sample.contributions);
   202 	float newLY = 0.0f;
   203 	for(u_int i = 0; i < newContributions.size(); ++i)
   204 		newLY += newContributions[i].color.Y();
   205 	if (mutation == 0U || mutation == ~0U) {
   206 		if (weight > 0.f) {
   207 			// Add accumulated contribution of previous reference sample
   208 			weight *= quantum / LY;
   209 			if (!isinf(weight) && LY > 0.f) {
   210 				for(u_int i = 0; i < oldContributions.size(); ++i) {
   211 					// Radiance - added new use of contributionpool/buffers
   212 					if(&oldContributions && !contribBuffer->Add(&oldContributions[i], weight)) {
   213 						contribBuffer = film->scene->contribPool->Next(contribBuffer);
   214 						contribBuffer->Add(&oldContributions[i], weight);
   215 					}
   216 				}
   217 			}
   218 			weight = 0.f;
   219 		}
   220 		if (mutation == ~0U) {
   221 			if (!(newLY > 0.f)) {
   222 				newContributions.clear();
   223 				return;
   224 			}
   225 			contribBuffer->AddSampleCount(1.f);
   226 			++sampleCount;
   227 			totalLY += newLY;
   228 			const float meanIntensity = totalLY > 0. ? static_cast<float>(totalLY / sampleCount) : 1.f;
   229 			// calculate the number of chains on a new seed
   230 			quantum = newLY / meanIntensity;
   231 			numChains = max(1U, Floor2UInt(quantum + .5f));
   232 			// The following line avoids to block on a pixel
   233 			// if the initial sample is extremely bright
   234 			numChains = min(numChains, totalMutations);
   235 			quantum /= (numChains * totalMutations);
   236 			baseLY = newLY;
   237 			baseContributions = newContributions;
   238 			baseImage[0] = sample.imageX;
   239 			baseImage[1] = sample.imageY;
   240 			baseImage[2] = sample.lensU;
   241 			baseImage[3] = sample.lensV;
   242 			baseImage[4] = sample.time;
   243 			baseImage[5] = sample.wavelengths;
   244 			for (u_int i = SAMPLE_FLOATS; i < totalSamples; ++i)
   245 				baseImage[i] = sample.oneD[0][i - SAMPLE_FLOATS];
   246 			for (u_int i = 0 ; i < totalTimes; ++i)
   247 				baseTimeImage[i] = sample.timexD[0][i];
   248 			mutation = 0;
   249 			newContributions.clear();
   250 			return;
   251 		}
   252 		LY = baseLY;
   253 		oldContributions = baseContributions;
   254 	}
   255 	// calculate accept probability from old and new image sample
   256 	float accProb;
   257 	if (LY > 0.f)
   258 		accProb = min(1.f, newLY / LY);
   259 	else
   260 		accProb = 1.f;
   261 	float newWeight = accProb;
   262 	weight += 1.f - accProb;
   263 
   264 	// try accepting of the new sample
   265 	if (accProb == 1.f || tspack->rng->floatValue() < accProb) {
   266 		// Add accumulated contribution of previous reference sample
   267 		weight *= quantum / LY;
   268 		if (!isinf(weight) && LY > 0.f) {
   269 			for(u_int i = 0; i < oldContributions.size(); ++i) {
   270 				// Radiance - added new use of contributionpool/buffers
   271 				if(&oldContributions && !contribBuffer->Add(&oldContributions[i], weight)) {
   272 					contribBuffer = film->scene->contribPool->Next(contribBuffer);
   273 					contribBuffer->Add(&oldContributions[i], weight);
   274 				}
   275 			}
   276 		}
   277 		weight = newWeight;
   278 		LY = newLY;
   279 		oldContributions = newContributions;
   280 
   281 		// Save new contributions for reference
   282 		sampleImage[0] = sample.imageX;
   283 		sampleImage[1] = sample.imageY;
   284 		sampleImage[2] = sample.lensU;
   285 		sampleImage[3] = sample.lensV;
   286 		sampleImage[4] = sample.time;
   287 		sampleImage[5] = sample.wavelengths;
   288 		for (u_int i = SAMPLE_FLOATS; i < totalSamples; ++i)
   289 			sampleImage[i] = sample.oneD[0][i - SAMPLE_FLOATS];
   290 		for (u_int i = 0 ; i < totalTimes; ++i)
   291 			timeImage[i] = sample.timexD[0][i];
   292 		stamp = sample.stamp;
   293 		currentImage = sampleImage;
   294 		currentTimeImage = timeImage;
   295 	} else {
   296 		// Add contribution of new sample before rejecting it
   297 		newWeight *= quantum / newLY;
   298 		if (!isinf(newWeight) && newLY > 0.f) {
   299 			for(u_int i = 0; i < newContributions.size(); ++i) {
   300 				// Radiance - added new use of contributionpool/buffers
   301 				if(!contribBuffer->Add(&newContributions[i], newWeight)) {
   302 					contribBuffer = film->scene->contribPool->Next(contribBuffer);
   303 					contribBuffer->Add(&newContributions[i], newWeight);
   304 				}
   305 			}
   306 		}
   307 
   308 		// Restart from previous reference
   309 		for (u_int i = 0; i < totalTimes; ++i)
   310 			sample.timexD[0][i] = currentTimeImage[i];
   311 		sample.stamp = stamp;
   312 	}
   313 	if (++mutation >= totalMutations) {
   314 		mutation = 0;
   315 		if (++chain >= numChains) {
   316 			chain = 0;
   317 			mutation = ~0U;
   318 		}
   319 	}
   320 	newContributions.clear();
   321 }
   322 
   323 Sampler* ERPTSampler::CreateSampler(const ParamSet &params, const Film *film)
   324 {
   325 	int xStart, xEnd, yStart, yEnd;
   326 	film->GetSampleExtent(&xStart, &xEnd, &yStart, &yEnd);
   327 	int totMutations = params.FindOneInt("chainlength", 100);	// number of mutations from a given seed
   328 	float range = params.FindOneFloat("mutationrange", (xEnd - xStart + yEnd - yStart) / 50.f);	// maximum distance in pixel for a small mutation
   329 	string base = params.FindOneString("basesampler", "random");	// sampler for new chain seed
   330 	Sampler *sampler = MakeSampler(base, params, film);
   331 	if (sampler == NULL) {
   332 		luxError(LUX_SYSTEM, LUX_SEVERE, "ERPTSampler: Could not obtain a valid sampler");
   333 		return NULL;
   334 	}
   335 	return new ERPTSampler(max(totMutations, 0), range, sampler);
   336 }
   337 
   338 static DynamicLoader::RegisterSampler<ERPTSampler> r("erpt");