#include "SampleGenerator.h" #include "common.h" using namespace std; using namespace CHCDemoEngine; HaltonSequence SphericalSampleGenerator3::sHalton; HaltonSequence PoissonDiscSampleGenerator2::sHalton; HaltonSequence RandomSampleGenerator2::sHalton; HaltonSequence QuadraticDiscSampleGenerator2::sHalton; SampleGenerator::SampleGenerator(int numSamples, float radius): mNumSamples(numSamples), mRadius(radius) {} PoissonDiscSampleGenerator2::PoissonDiscSampleGenerator2(int numSamples, float radius): SampleGenerator(numSamples, radius) {} void PoissonDiscSampleGenerator2::Generate(float *samples) const { // this is a hacky poisson sampling generator which does random dart-throwing on a disc. // as a savety criterium, the min distance requirement is relaxed if we are not // able to place any dart for a number of tries // the solution is a possion sampling with respect to the adjusted min distance // better solutions have been proposed, i.e., using hierarchical sampling const float maxTries = 1000; const float f_reduction = 0.9f; //static HaltonSequence halton; float r[2]; // generates poisson distribution on disc // start with some threshold. best case: all samples lie on the circumference //const float minDist = 2.0f * mRadius / sqrt((float)mNumSamples); const float eps = 0.2f; const float minDist = 2.0f * mRadius * M_PI * (1.0f - eps) / (float)mNumSamples; float sqrMinDist = minDist * minDist; //cout << "minDist before= " << minDist << endl; Sample2 *s = (Sample2 *)samples; int totalTries = 0; // check if on disc for (int i = 0; i < mNumSamples; ++ i) { int tries = 0; // repeat until valid sample was found while (1) { ++ tries; ++ totalTries; // note: should use halton, but seems somewhat broken //r[0] = RandomValue(.0f, mRadius); //r[1] = RandomValue(.0f, mRadius); sHalton.GetNext(2, r); // scale to -1 .. 1 const float rx = r[0] * 2.0f - 1.0f; const float ry = r[1] * 2.0f - 1.0f; // check if in disk, else exit early const float distanceSquared = rx * rx + ry * ry; if ((rx * rx + ry * ry > mRadius * mRadius) // also avoid case that sample exactly in center || (distanceSquared <= 1e-3f) ) continue; bool sampleValid = true; // check poisson property for (int j = 0; ((j < i) && sampleValid); ++ j) { const float dist = (s[j].x - rx) * (s[j].x - rx) + (s[j].y - ry) * (s[j].y - ry); if (dist < sqrMinDist) sampleValid = false; } if (sampleValid) { s[i].x = rx; s[i].y = ry; break; } if (tries > maxTries) { sqrMinDist *= f_reduction; tries = 0; } } } //cout << "minDist after= " << sqrt(sqrMinDist) << " #tries: " << totalTries << endl; } RandomSampleGenerator2::RandomSampleGenerator2(int numSamples, float radius): SampleGenerator(numSamples, radius) {} void RandomSampleGenerator2::Generate(float *samples) const { Sample2 *s = (Sample2 *)samples; int numSamples = 0; float r[2]; while (numSamples < mNumSamples) { //r[0] = RandomValue(-mRadius, mRadius); //r[1] = RandomValue(-mRadius, mRadius); sHalton.GetNext(2, r); const float rx = r[0] * 2.0f - 1.0f; const float ry = r[1] * 2.0f - 1.0f; // check if in disk, else exit early if (rx * rx + ry * ry > mRadius * mRadius) continue; s[numSamples].x = rx; s[numSamples].y = ry; ++ numSamples; } } SphericalSampleGenerator3::SphericalSampleGenerator3(int numSamples, float radius): SampleGenerator(numSamples, radius) {} void SphericalSampleGenerator3::Generate(float *samples) const { float r[2]; Sample3 *s = (Sample3 *)samples; for (int i = 0; i < mNumSamples; ++ i) { r[0] = RandomValue(0, 1); r[1] = RandomValue(0, 1); //sHalton.GetNext(2, r); // create stratified samples over sphere const float theta = 2.0f * acos(sqrt(1.0f - r[0])); const float phi = 2.0f * M_PI * r[1]; s[i].x = mRadius * sin(theta) * cos(phi); s[i].y = mRadius * sin(theta) * sin(phi); s[i].z = mRadius * cos(theta); } } QuadraticDiscSampleGenerator2::QuadraticDiscSampleGenerator2(int numSamples, float radius): SampleGenerator(numSamples, radius) {} void QuadraticDiscSampleGenerator2::Generate(float *samples) const { #if 0 float r[2]; Sample2 *s = (Sample2 *)samples; for (int i = 0; i < mNumSamples; ++ i) { //r[0] = samples[i * 2]; //r[1] = samples[i * 2 + 1]; sHalton.GetNext(2, r); // create samples over disc: the sample density // decreases quadratically with the distance to the origin s[i].x = mRadius * r[1] * sin(2.0f * M_PI * r[0]); s[i].y = mRadius * r[1] * cos(2.0f * M_PI * r[0]); } #else PoissonDiscSampleGenerator2 poisson(mNumSamples, 1.0f); poisson.Generate(samples); Sample2 *s = (Sample2 *)samples; // multiply with lenght to get quadratic dependence on the distance for (int i = 0; i < mNumSamples; ++ i) { Sample2 &spl = s[i]; float len = sqrt(spl.x * spl.x + spl.y * spl.y); spl.x *= len * mRadius; spl.y *= len * mRadius; } #endif }