samplers/erpt.cpp
author Tom Bech <tom.bech@gmail.com>
Mon Mar 22 21:55:10 2010 +0100 (2010-03-22)
changeset 2081 43981caab6b9
parent 1643 eac52d5c6a9e
child 2174 4476a60abb5a
permissions -rwxr-xr-x
Switch to Qt GUI as default, wx is still there but built as "luxrenderwx"
jeanphi@387
     1
/***************************************************************************
lordcrc@1510
     2
 *   Copyright (C) 1998-2009 by authors (see AUTHORS.txt )                 *
jeanphi@387
     3
 *                                                                         *
radiance29@447
     4
 *   This file is part of LuxRender.                                       *
jeanphi@387
     5
 *                                                                         *
jeanphi@387
     6
 *   Lux Renderer is free software; you can redistribute it and/or modify  *
jeanphi@387
     7
 *   it under the terms of the GNU General Public License as published by  *
jeanphi@387
     8
 *   the Free Software Foundation; either version 3 of the License, or     *
jeanphi@387
     9
 *   (at your option) any later version.                                   *
jeanphi@387
    10
 *                                                                         *
jeanphi@387
    11
 *   Lux Renderer is distributed in the hope that it will be useful,       *
jeanphi@387
    12
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
jeanphi@387
    13
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
jeanphi@387
    14
 *   GNU General Public License for more details.                          *
jeanphi@387
    15
 *                                                                         *
jeanphi@387
    16
 *   You should have received a copy of the GNU General Public License     *
jeanphi@387
    17
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
jeanphi@387
    18
 *                                                                         *
jeanphi@387
    19
 *   This project is based on PBRT ; see http://www.pbrt.org               *
radiance29@447
    20
 *   Lux Renderer website : http://www.luxrender.net                       *
jeanphi@387
    21
 ***************************************************************************/
jeanphi@387
    22
jeanphi@411
    23
// initial metropolis light transport sample integrator
jeanphi@411
    24
// by radiance
jeanphi@411
    25
jeanphi@387
    26
// TODO: add scaling of output image samples
jeanphi@387
    27
jeanphi@387
    28
// erpt.cpp*
jeanphi@387
    29
#include "erpt.h"
jeanphi@387
    30
#include "dynload.h"
radiance29@974
    31
#include "scene.h"
jeanphi@942
    32
#include "error.h"
jeanphi@387
    33
jeanphi@387
    34
using namespace lux;
jeanphi@387
    35
jeanphi@1643
    36
#define SAMPLE_FLOATS 6
jeanphi@451
    37
jeanphi@387
    38
// mutate a value in the range [0-1]
radiance29@896
    39
static float mutate(const float x, const float randomValue)
jeanphi@387
    40
{
jeanphi@495
    41
	static const float s1 = 1.f / 1024.f, s2 = 1.f / 16.f;
jeanphi@495
    42
	float dx = s2 * powf(s1 / s2, fabsf(2.f * randomValue - 1.f));
jeanphi@495
    43
	if (randomValue < 0.5f) {
jeanphi@387
    44
		float x1 = x + dx;
jeanphi@495
    45
		return (x1 > 1.f) ? x1 - 1.f : x1;
jeanphi@387
    46
	} else {
jeanphi@387
    47
		float x1 = x - dx;
jeanphi@495
    48
		return (x1 < 0.f) ? x1 + 1.f : x1;
jeanphi@387
    49
	}
jeanphi@387
    50
}
jeanphi@387
    51
jeanphi@387
    52
// mutate a value in the range [min-max]
radiance29@896
    53
static float mutateScaled(const float x, const float randomValue, const float mini, const float maxi, const float range)
jeanphi@387
    54
{
jeanphi@1661
    55
	float dx = range * expf(-logf(2.f * range) * fabsf(2.f * randomValue - 1.f));
jeanphi@495
    56
	if (randomValue < 0.5f) {
jeanphi@392
    57
		float x1 = x + dx;
jeanphi@411
    58
		return (x1 > maxi) ? x1 - maxi + mini : x1;
jeanphi@392
    59
	} else {
jeanphi@392
    60
		float x1 = x - dx;
jeanphi@411
    61
		return (x1 < mini) ? x1 - mini + maxi : x1;
jeanphi@392
    62
	}
jeanphi@387
    63
}
jeanphi@387
    64
jeanphi@387
    65
// Metropolis method definitions
jeanphi@1661
    66
ERPTSampler::ERPTSampler(u_int totMutations, float rng,
jeanphi@901
    67
	Sampler *sampler) :
jeanphi@901
    68
 Sampler(sampler->xPixelStart, sampler->xPixelEnd,
jeanphi@901
    69
	sampler->yPixelStart, sampler->yPixelEnd, sampler->samplesPerPixel),
jeanphi@1557
    70
 normalSamples(0), totalSamples(0), totalTimes(0), totalMutations(totMutations),
jeanphi@1661
    71
 range(rng), baseSampler(sampler),
jeanphi@901
    72
 baseImage(NULL), sampleImage(NULL), currentImage(NULL),
jeanphi@1557
    73
 baseTimeImage(NULL), timeImage(NULL), currentTimeImage(NULL), offset(NULL),
jeanphi@1661
    74
 numChains(0), chain(0), mutation(~0U), stamp(0),
jeanphi@1557
    75
 baseLY(0.f), quantum(0.f), weight(0.f), LY(0.f), alpha(0.f),
jeanphi@1557
    76
 totalLY(0.), sampleCount(0.)
jeanphi@387
    77
{
jeanphi@387
    78
}
jeanphi@387
    79
dade916@644
    80
ERPTSampler::~ERPTSampler() {
dade916@644
    81
	FreeAligned(sampleImage);
dade916@644
    82
	FreeAligned(baseImage);
dade916@644
    83
	FreeAligned(timeImage);
jeanphi@901
    84
	FreeAligned(baseTimeImage);
jeanphi@901
    85
	delete baseSampler;
dade916@644
    86
}
dade916@644
    87
jeanphi@387
    88
// Copy
jeanphi@387
    89
ERPTSampler* ERPTSampler::clone() const
jeanphi@387
    90
{
jeanphi@387
    91
	ERPTSampler *newSampler = new ERPTSampler(*this);
jeanphi@901
    92
	newSampler->baseSampler = baseSampler->clone();
jeanphi@411
    93
	newSampler->totalSamples = 0;
jeanphi@411
    94
	newSampler->sampleImage = NULL;
jeanphi@387
    95
	return newSampler;
jeanphi@387
    96
}
jeanphi@387
    97
jeanphi@411
    98
static void initERPT(ERPTSampler *sampler, const Sample *sample)
jeanphi@411
    99
{
jeanphi@411
   100
	u_int i;
jeanphi@451
   101
	sampler->normalSamples = SAMPLE_FLOATS;
jeanphi@411
   102
	for (i = 0; i < sample->n1D.size(); ++i)
jeanphi@411
   103
		sampler->normalSamples += sample->n1D[i];
jeanphi@411
   104
	for (i = 0; i < sample->n2D.size(); ++i)
jeanphi@411
   105
		sampler->normalSamples += 2 * sample->n2D[i];
jeanphi@411
   106
	sampler->totalSamples = sampler->normalSamples;
jeanphi@1661
   107
	sampler->offset = new u_int[sample->nxD.size()];
jeanphi@411
   108
	sampler->totalTimes = 0;
jeanphi@411
   109
	for (i = 0; i < sample->nxD.size(); ++i) {
jeanphi@411
   110
		sampler->offset[i] = sampler->totalSamples;
jeanphi@411
   111
		sampler->totalTimes += sample->nxD[i];
jeanphi@411
   112
		sampler->totalSamples += sample->dxD[i] * sample->nxD[i];
jeanphi@411
   113
	}
jeanphi@1339
   114
	sampler->sampleImage = AllocAligned<float>(sampler->totalSamples);
jeanphi@1339
   115
	sampler->baseImage = AllocAligned<float>(sampler->totalSamples);
jeanphi@1339
   116
	sampler->timeImage = AllocAligned<int>(sampler->totalTimes);
jeanphi@1339
   117
	sampler->baseTimeImage = AllocAligned<int>(sampler->totalTimes);
jeanphi@901
   118
	sampler->baseSampler->SetTsPack(sampler->tspack);
jeanphi@1019
   119
	sampler->baseSampler->SetFilm(sampler->film);
jeanphi@1661
   120
	sampler->mutation = ~0U;
radiance29@974
   121
radiance29@974
   122
	// Fetch first contribution buffer from pool
radiance29@974
   123
	sampler->contribBuffer = sampler->film->scene->contribPool->Next(NULL);
jeanphi@411
   124
}
jeanphi@411
   125
jeanphi@387
   126
// interface for new ray/samples from scene
jeanphi@387
   127
bool ERPTSampler::GetNextSample(Sample *sample, u_int *use_pos)
jeanphi@387
   128
{
jeanphi@411
   129
	sample->sampler = this;
jeanphi@387
   130
	if (sampleImage == NULL) {
jeanphi@411
   131
		initERPT(this, sample);
jeanphi@411
   132
	}
dade916@618
   133
jeanphi@1661
   134
	if (mutation == ~0U) {
jeanphi@901
   135
		// Dade - we are at a valid checkpoint where we can stop the
jeanphi@901
   136
		// rendering. Check if we have enough samples per pixel in the film.
jeanphi@901
   137
		if (film->enoughSamplePerPixel)
jeanphi@901
   138
			return false;
jeanphi@901
   139
jeanphi@901
   140
		const bool ret = baseSampler->GetNextSample(sample, use_pos);
jeanphi@901
   141
		sample->sampler = this;
jeanphi@1661
   142
		for (u_int i = 0; i < totalTimes; ++i)
jeanphi@901
   143
			sample->timexD[0][i] = -1;
jeanphi@901
   144
		sample->stamp = 0;
jeanphi@901
   145
		currentImage = baseImage;
jeanphi@901
   146
		currentTimeImage = baseTimeImage;
jeanphi@901
   147
		stamp = 0;
jeanphi@901
   148
		return ret;
jeanphi@411
   149
	} else {
jeanphi@411
   150
		if (mutation == 0) {
jeanphi@411
   151
			// *** new chain ***
jeanphi@1661
   152
			for (u_int i = 0; i < totalTimes; ++i)
jeanphi@901
   153
				sample->timexD[0][i] = baseTimeImage[i];
jeanphi@414
   154
			sample->stamp = 0;
jeanphi@901
   155
			currentImage = baseImage;
jeanphi@901
   156
			currentTimeImage = baseTimeImage;
jeanphi@901
   157
			stamp = 0;
jeanphi@406
   158
		}
jeanphi@387
   159
		// *** small mutation ***
jeanphi@387
   160
		// mutate current sample
jeanphi@1661
   161
		sample->imageX = mutateScaled(currentImage[0], tspack->rng->floatValue(), xPixelStart, xPixelEnd, range);
jeanphi@1661
   162
		sample->imageY = mutateScaled(currentImage[1], tspack->rng->floatValue(), yPixelStart, yPixelEnd, range);
jeanphi@1661
   163
		sample->lensU = mutate(currentImage[2], tspack->rng->floatValue());
jeanphi@1661
   164
		sample->lensV = mutate(currentImage[3], tspack->rng->floatValue());
jeanphi@1661
   165
		sample->time = mutate(currentImage[4], tspack->rng->floatValue());
jeanphi@1661
   166
		sample->wavelengths = mutate(currentImage[5], tspack->rng->floatValue());
jeanphi@1661
   167
		for (u_int i = SAMPLE_FLOATS; i < normalSamples; ++i)
jeanphi@901
   168
				sample->oneD[0][i - SAMPLE_FLOATS] = mutate(currentImage[i], tspack->rng->floatValue());
jeanphi@411
   169
		++(sample->stamp);
jeanphi@387
   170
	}
jeanphi@387
   171
jeanphi@1661
   172
	return true;
jeanphi@387
   173
}
jeanphi@387
   174
jeanphi@406
   175
float *ERPTSampler::GetLazyValues(Sample *sample, u_int num, u_int pos)
jeanphi@406
   176
{
jeanphi@901
   177
	const u_int size = sample->dxD[num];
jeanphi@901
   178
	float *data = sample->xD[num] + pos * size;
jeanphi@1661
   179
	const int stampLimit = sample->stamp;
jeanphi@901
   180
	if (sample->timexD[num][pos] != stampLimit) {
jeanphi@411
   181
		if (sample->timexD[num][pos] == -1) {
jeanphi@1557
   182
			baseSampler->GetLazyValues(sample, num, pos);
jeanphi@1557
   183
			sample->timexD[num][pos] = 0;
jeanphi@411
   184
		} else {
jeanphi@1661
   185
			const u_int start = offset[num] + pos * size;
jeanphi@901
   186
			float *image = currentImage + start;
jeanphi@901
   187
			for (u_int i = 0; i < size; ++i)
jeanphi@480
   188
				data[i] = image[i];
jeanphi@411
   189
		}
jeanphi@901
   190
		for (int &time = sample->timexD[num][pos]; time < stampLimit; ++time) {
jeanphi@901
   191
			for (u_int i = 0; i < size; ++i)
radiance29@896
   192
				data[i] = mutate(data[i], tspack->rng->floatValue());
jeanphi@411
   193
		}
jeanphi@406
   194
	}
jeanphi@406
   195
	return data;
jeanphi@406
   196
}
jeanphi@406
   197
jeanphi@387
   198
// interface for adding/accepting a new image sample.
jeanphi@495
   199
void ERPTSampler::AddSample(const Sample &sample)
jeanphi@495
   200
{
radiance29@974
   201
	vector<Contribution> &newContributions(sample.contributions);
jeanphi@495
   202
	float newLY = 0.0f;
jeanphi@526
   203
	for(u_int i = 0; i < newContributions.size(); ++i)
jeanphi@1520
   204
		newLY += newContributions[i].color.Y();
jeanphi@1661
   205
	if (mutation == 0U || mutation == ~0U) {
jeanphi@901
   206
		if (weight > 0.f) {
jeanphi@901
   207
			// Add accumulated contribution of previous reference sample
jeanphi@1557
   208
			weight *= quantum / LY;
jeanphi@901
   209
			if (!isinf(weight) && LY > 0.f) {
jeanphi@901
   210
				for(u_int i = 0; i < oldContributions.size(); ++i) {
radiance29@974
   211
					// Radiance - added new use of contributionpool/buffers
radiance29@974
   212
					if(&oldContributions && !contribBuffer->Add(&oldContributions[i], weight)) {
radiance29@974
   213
						contribBuffer = film->scene->contribPool->Next(contribBuffer);
radiance29@974
   214
						contribBuffer->Add(&oldContributions[i], weight);
radiance29@974
   215
					}
jeanphi@901
   216
				}
jeanphi@496
   217
			}
jeanphi@901
   218
			weight = 0.f;
jeanphi@495
   219
		}
jeanphi@1661
   220
		if (mutation == ~0U) {
jeanphi@1121
   221
			contribBuffer->AddSampleCount(1.f);
jeanphi@1557
   222
			++sampleCount;
jeanphi@1541
   223
			if (!(newLY > 0.f)) {
jeanphi@1541
   224
				newContributions.clear();
jeanphi@880
   225
				return;
jeanphi@1541
   226
			}
jeanphi@1557
   227
			totalLY += newLY;
jeanphi@1661
   228
			const float meanIntensity = totalLY > 0. ? static_cast<float>(totalLY / sampleCount) : 1.f;
jeanphi@1557
   229
			// calculate the number of chains on a new seed
jeanphi@1557
   230
			quantum = newLY / meanIntensity;
jeanphi@1661
   231
			numChains = max(1U, Floor2UInt(quantum + .5f));
jeanphi@1019
   232
			if (numChains > 100) {
jeanphi@1019
   233
				printf("%d chains -> %d\n", numChains, totalMutations);
jeanphi@1019
   234
				numChains = totalMutations;
jeanphi@1019
   235
			}
jeanphi@1557
   236
			quantum /= (numChains * totalSamples);
jeanphi@880
   237
			baseLY = newLY;
jeanphi@880
   238
			baseContributions = newContributions;
jeanphi@901
   239
			baseImage[0] = sample.imageX;
jeanphi@901
   240
			baseImage[1] = sample.imageY;
jeanphi@901
   241
			baseImage[2] = sample.lensU;
jeanphi@901
   242
			baseImage[3] = sample.lensV;
jeanphi@901
   243
			baseImage[4] = sample.time;
jeanphi@901
   244
			baseImage[5] = sample.wavelengths;
jeanphi@1661
   245
			for (u_int i = SAMPLE_FLOATS; i < totalSamples; ++i)
jeanphi@901
   246
				baseImage[i] = sample.oneD[0][i - SAMPLE_FLOATS];
jeanphi@1661
   247
			for (u_int i = 0 ; i < totalTimes; ++i)
jeanphi@901
   248
				baseTimeImage[i] = sample.timexD[0][i];
jeanphi@880
   249
			mutation = 0;
jeanphi@1541
   250
			newContributions.clear();
jeanphi@880
   251
			return;
jeanphi@880
   252
		}
jeanphi@880
   253
		LY = baseLY;
jeanphi@880
   254
		oldContributions = baseContributions;
jeanphi@880
   255
	}
jeanphi@880
   256
	// calculate accept probability from old and new image sample
jeanphi@1557
   257
	float accProb;
jeanphi@1557
   258
	if (LY > 0.f)
jeanphi@1557
   259
		accProb = min(1.f, newLY / LY);
jeanphi@1557
   260
	else
jeanphi@1557
   261
		accProb = 1.f;
jeanphi@880
   262
	float newWeight = accProb;
jeanphi@880
   263
	weight += 1.f - accProb;
jeanphi@880
   264
jeanphi@880
   265
	// try accepting of the new sample
jeanphi@1557
   266
	if (accProb == 1.f || tspack->rng->floatValue() < accProb) {
jeanphi@880
   267
		// Add accumulated contribution of previous reference sample
jeanphi@1557
   268
		weight *= quantum / LY;
jeanphi@880
   269
		if (!isinf(weight) && LY > 0.f) {
jeanphi@880
   270
			for(u_int i = 0; i < oldContributions.size(); ++i) {
radiance29@974
   271
				// Radiance - added new use of contributionpool/buffers
radiance29@974
   272
				if(&oldContributions && !contribBuffer->Add(&oldContributions[i], weight)) {
radiance29@974
   273
					contribBuffer = film->scene->contribPool->Next(contribBuffer);
radiance29@974
   274
					contribBuffer->Add(&oldContributions[i], weight);
jeanphi@1557
   275
				}
jeanphi@880
   276
			}
jeanphi@880
   277
		}
jeanphi@411
   278
		weight = newWeight;
jeanphi@495
   279
		LY = newLY;
jeanphi@495
   280
		oldContributions = newContributions;
jeanphi@880
   281
jeanphi@880
   282
		// Save new contributions for reference
jeanphi@411
   283
		sampleImage[0] = sample.imageX;
jeanphi@411
   284
		sampleImage[1] = sample.imageY;
jeanphi@387
   285
		sampleImage[2] = sample.lensU;
jeanphi@387
   286
		sampleImage[3] = sample.lensV;
jeanphi@387
   287
		sampleImage[4] = sample.time;
jeanphi@451
   288
		sampleImage[5] = sample.wavelengths;
jeanphi@1661
   289
		for (u_int i = SAMPLE_FLOATS; i < totalSamples; ++i)
jeanphi@451
   290
			sampleImage[i] = sample.oneD[0][i - SAMPLE_FLOATS];
jeanphi@1661
   291
		for (u_int i = 0 ; i < totalTimes; ++i)
jeanphi@411
   292
			timeImage[i] = sample.timexD[0][i];
jeanphi@411
   293
		stamp = sample.stamp;
jeanphi@901
   294
		currentImage = sampleImage;
jeanphi@901
   295
		currentTimeImage = timeImage;
jeanphi@387
   296
	} else {
jeanphi@495
   297
		// Add contribution of new sample before rejecting it
jeanphi@1557
   298
		newWeight *= quantum / newLY;
jeanphi@499
   299
		if (!isinf(newWeight) && newLY > 0.f) {
jeanphi@496
   300
			for(u_int i = 0; i < newContributions.size(); ++i) {
radiance29@974
   301
				// Radiance - added new use of contributionpool/buffers
radiance29@974
   302
				if(!contribBuffer->Add(&newContributions[i], newWeight)) {
radiance29@974
   303
					contribBuffer = film->scene->contribPool->Next(contribBuffer);
radiance29@974
   304
					contribBuffer->Add(&newContributions[i], newWeight);
radiance29@974
   305
				}
jeanphi@496
   306
			}
jeanphi@495
   307
		}
jeanphi@880
   308
jeanphi@495
   309
		// Restart from previous reference
jeanphi@1661
   310
		for (u_int i = 0; i < totalTimes; ++i)
jeanphi@901
   311
			sample.timexD[0][i] = currentTimeImage[i];
jeanphi@411
   312
		sample.stamp = stamp;
jeanphi@387
   313
	}
jeanphi@411
   314
	if (++mutation >= totalMutations) {
jeanphi@411
   315
		mutation = 0;
jeanphi@880
   316
		if (++chain >= numChains) {
jeanphi@411
   317
			chain = 0;
jeanphi@1661
   318
			mutation = ~0U;
jeanphi@880
   319
		}
jeanphi@392
   320
	}
jeanphi@1541
   321
	newContributions.clear();
jeanphi@387
   322
}
jeanphi@387
   323
jeanphi@387
   324
Sampler* ERPTSampler::CreateSampler(const ParamSet &params, const Film *film)
jeanphi@387
   325
{
jeanphi@411
   326
	int xStart, xEnd, yStart, yEnd;
jeanphi@411
   327
	film->GetSampleExtent(&xStart, &xEnd, &yStart, &yEnd);
jeanphi@901
   328
	int totMutations = params.FindOneInt("chainlength", 100);	// number of mutations from a given seed
jeanphi@1661
   329
	float range = params.FindOneFloat("mutationrange", (xEnd - xStart + yEnd - yStart) / 50.f);	// maximum distance in pixel for a small mutation
jeanphi@901
   330
	string base = params.FindOneString("basesampler", "random");	// sampler for new chain seed
jeanphi@901
   331
	Sampler *sampler = MakeSampler(base, params, film);
jeanphi@901
   332
	if (sampler == NULL) {
radiance29@974
   333
		luxError(LUX_SYSTEM, LUX_SEVERE, "ERPTSampler: Could not obtain a valid sampler");
jeanphi@901
   334
		return NULL;
jeanphi@901
   335
	}
jeanphi@1661
   336
	return new ERPTSampler(max(totMutations, 0), range, sampler);
jeanphi@387
   337
}
jeanphi@387
   338
jeanphi@901
   339
static DynamicLoader::RegisterSampler<ERPTSampler> r("erpt");