#include "SampleGenerator.h" #include "common.h" using namespace std; using namespace CHCDemoEngine; SampleGenerator::SampleGenerator(int numSamples, float radius): mNumSamples(numSamples), mRadius(radius) { mHalton = new HaltonSequence(2); } SampleGenerator::~SampleGenerator() { DEL_PTR(mHalton); } PoissonDiscSampleGenerator2D::PoissonDiscSampleGenerator2D(int numSamples, float radius): SampleGenerator(numSamples, radius) {} void PoissonDiscSampleGenerator2D::Generate(float *samples) const { // Poisson disc sampling generator using relaxation // dart-throwing as proposed by McCool et al. // 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 // this sampling scheme has the benefit that it is hierarchical int maxTries = 1000; const float f_reduction = 0.95f; float r[2]; // the maximal possible radius: our radius is a fraction of this radius // this is used as a measure of the quality of distribution of the point samples const float rmax = 2.0f * mRadius * sqrt(1.0f / (2.0f * sqrt(3.0f) * mNumSamples)); // generates poisson distribution on disc // start with some thresholds: all samples lie on the circumference of circle float minDist = 2.0f * rmax; float sqrMinDist = minDist * minDist; int tries = 0; //cout << "minDist before= " << minDist << endl; Sample2 *s = (Sample2 *)samples; // check if on disc for (int i = 0; i < mNumSamples; ++ i) { // repeat until valid sample was found while (1) { // q: should we use halton or does it conflict with the poisson disc properties? r[0] = RandomValue(0, 1); r[1] = RandomValue(0, 1); //mHalton->GetNext(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; } ++ tries; if (tries > maxTries) { minDist *= f_reduction; sqrMinDist = minDist * minDist; maxTries += 1000; } } } //cout << "minDist after= " << (float)minDist / mNumSamples<< " #tries: " << tries << endl; } RandomSampleGenerator2D::RandomSampleGenerator2D(int numSamples, float radius): SampleGenerator(numSamples, radius) {} void RandomSampleGenerator2D::Generate(float *samples) const { Sample2 *s = (Sample2 *)samples; int numSamples = 0; float r[2]; while (numSamples < mNumSamples) { mHalton->GetNext(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; } } SphericalSampleGenerator3D::SphericalSampleGenerator3D(int numSamples, float radius): SampleGenerator(numSamples, radius) {} void SphericalSampleGenerator3D::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); // 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); } } QuadraticDiscSampleGenerator2D::QuadraticDiscSampleGenerator2D(int numSamples, float radius): //PoissonDiscSampleGenerator2D(numSamples, radius) RandomSampleGenerator2D(numSamples, radius) {} void QuadraticDiscSampleGenerator2D::Generate(float *samples) const { #if 0 float r[2]; Sample2 *s = (Sample2 *)samples; for (int i = 0; i < mNumSamples; ++ i) { mHalton->GetNext(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 //PoissonDiscSampleGenerator2D::Generate(samples); RandomSampleGenerator2D::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 }