示例#1
0
void BestCandidate2D(float table[][2], int totalSamples,
                     RNG &rng, SampleGrid *grid) {
    SampleGrid localGrid;
    if (!grid) grid = &localGrid;
    ProgressReporter
    progress(totalSamples-1, "Throwing Darts");
    // Generate first 2D sample arbitrarily
    table[0][0] = rng.RandomFloat();
    table[0][1] = rng.RandomFloat();
    addSampleToGrid(table, 0, grid);
    for (int currentSample = 1; currentSample < totalSamples;
            ++currentSample) {
        // Generate next best 2D image sample
        float maxDist2 = 0.;
        int numCandidates = 500 * currentSample;
        for (int currentCandidate = 0;
                currentCandidate < numCandidates;
                ++currentCandidate) {
            // Generate a random candidate sample
            float candidate[2];
            candidate[0] = rng.RandomFloat();
            candidate[1] = rng.RandomFloat();

            // Loop over neighboring grid cells and check distances
            float sampleDist2 = INFINITY;
            int gu = GRID(candidate[0]), gv = GRID(candidate[1]);
            for (int du = -1; du <= 1; ++du) {
                for (int dv = -1; dv <= 1; ++dv) {
                    // Compute (u,v) grid cell to check
                    int u = gu + du, v = gv + dv;
                    if (u < 0)             u += BC_GRID_SIZE;
                    if (u >= BC_GRID_SIZE) u -= BC_GRID_SIZE;
                    if (v < 0)             v += BC_GRID_SIZE;
                    if (v >= BC_GRID_SIZE) v -= BC_GRID_SIZE;

                    // Update minimum squared distance from cell's samples
                    for (uint32_t g = 0; g < (*grid)[u][v].size(); ++g) {
                        int s = (*grid)[u][v][g];
                        float xdist = Wrapped1DDist(candidate[0], table[s][0]);
                        float ydist = Wrapped1DDist(candidate[1], table[s][1]);
                        float d2 = xdist*xdist + ydist*ydist;
                        sampleDist2 = min(sampleDist2, d2);
                    }
                }
            }

            // Keep this sample if it is the best one so far
            if (sampleDist2 > maxDist2) {
                maxDist2 = sampleDist2;
                table[currentSample][0] = candidate[0];
                table[currentSample][1] = candidate[1];
            }
        }
        addSampleToGrid(table, currentSample, grid);
        progress.Update();
    }
    progress.Done();
}
示例#2
0
bool HomogeneousVolumeDensity::SampleDirection(const Point &p, const Vector& wi,
        Vector& wo, float* pdf, RNG &rng) const {
    const Point Pobj = WorldToVolume(p);
    if(extent.Inside(Pobj)) {
        wo = SampleHG(wi, g, rng.RandomFloat(), rng.RandomFloat());
        *pdf = PhaseHG(wi, wo, g);
        return true;
    } else {
        return false;
    }
}
int main(int argc, char **argv)
{
	if (argc != 3) {
		cout << endl << "input format: sobol N D FILENAME" << endl << endl;
		cout << "The program prints the first N sobol points in D dimensions." << endl;
		cout << "The points are generated in graycode order." << endl;
		cout << "The primitive polynomials and initial direction numbers are" << endl
			<< "given by the input file FILENAME." << endl << endl;
		return 0;
	}

	int N = atoi(argv[1]);
	HenyeyGreensteinSampleGenerator hg(N);
	RNG rng;
	rng.Seed(3);
	vector<vector<SobolPoints3D *>> mpVec2nd;
	for(int i = 0 ; i < 10 ; i++)
	{
		vector<SobolPoints3D *> *mpVec = new vector<SobolPoints3D *>();
		for(int j = 0 ; j < 5 ; j++)
		{
			float wPrev[3];
			for(int k = 0 ; k < 3 ; k++)
				wPrev[k] = rng.RandomFloat();
			Vector _wPrev(wPrev[0], wPrev[1], wPrev[2]);
			_wPrev.Normalize();
			wPrev[0] = _wPrev.x; wPrev[1] = _wPrev.y; wPrev[2] = _wPrev.z;
			SobolPoints3D * mp = new SobolPoints3D(wPrev[0], wPrev[1], wPrev[2], 0.f);
			mpVec ->push_back(mp);
		}
		mpVec2nd.push_back(*mpVec);
	}
	vector<vector<SobolPoints3D *>> transformedPaths;
	Vector z(1.f,-1.f,1.5f);
	z.Normalize();
	// when z = (0, 1, 0), it will cause some problem!
	hg.TransformVector(z, mpVec2nd, transformedPaths);
	vector<SobolPoints3D *> paths;
	vector<SobolPoints3D *> pathsConverted;
	for(int i = 0 ; i < 20 ; i++) {
		SobolPoints3D * sp = new SobolPoints3D(rng.RandomFloat(), rng.RandomFloat(), rng.RandomFloat(), rng.RandomFloat());
		Vector v(sp ->operator Vector()); v.Normalize();
		sp ->x = v.x; sp ->y = v.y; sp ->z = v.z;
		paths.push_back(sp);
	}
	hg.TransformVector(z, paths, pathsConverted);
	//hg.PrintVectors(transformedPaths);
	hg.ToString(z, mpVec2nd, transformedPaths);
	//hg.ToString(z, paths, pathsConverted);

	// Later, make sure that I free the one dimensional vectors then 2 dimensional vectors. You must loop over the vector<vector<MediaPath *>>.

}
示例#4
0
Ray Persp::UnProject(int U, int V, RNG &rng){
	Ray result;
	/*Vec centerOfPixel = leftBottomVP + 
						Vec(U*xDelta,V*yDelta,0) + 
						Vec(xDelta/2.0f,yDelta/2.0f,.0f);*/

	float epsilon1 = rng.RandomFloat(); float epsilon2 = rng.RandomFloat();
	Vec targetPoint = leftBottomVP + Vec(U*xDelta,V*yDelta,0) + Vec( epsilon1*xDelta, epsilon2*yDelta,.0f);
	Vec dir = Vec(targetPoint.x, targetPoint.y, targetPoint.z);
	dir.norm();

	result = Mat4::Mul(this->viewMatInv,Ray(targetPoint, dir));
	return result;
}
示例#5
0
// Sampling Function Definitions
void StratifiedSample1D(float *samp, int nSamples, RNG &rng, bool jitter) {
    float invTot = 1.f / nSamples;
    for (int i = 0;  i < nSamples; ++i) {
        float delta = jitter ? rng.RandomFloat() : 0.5f;
        *samp++ = (i + delta) * invTot;
    }
}
示例#6
0
Spectrum UniformSampleOneLight(const Scene *scene,
        const Renderer *renderer, MemoryArena &arena, const Point &p,
        const Normal &n, const Vector &wo, float rayEpsilon, float time,
        BSDF *bsdf, const Sample *sample, RNG &rng, int lightNumOffset,
        const LightSampleOffsets *lightSampleOffset,
        const BSDFSampleOffsets *bsdfSampleOffset) {
    // Randomly choose a single light to sample, _light_
    int nLights = int(scene->lights.size());
    if (nLights == 0) return Spectrum(0.);
    int lightNum;
    if (lightNumOffset != -1)
        lightNum = Floor2Int(sample->oneD[lightNumOffset][0] * nLights);
    else
        lightNum = Floor2Int(rng.RandomFloat() * nLights);
    lightNum = min(lightNum, nLights-1);
    Light *light = scene->lights[lightNum];

    // Initialize light and bsdf samples for single light sample
    LightSample lightSample;
    BSDFSample bsdfSample;
    if (lightSampleOffset != NULL && bsdfSampleOffset != NULL) {
        lightSample = LightSample(sample, *lightSampleOffset, 0);
        bsdfSample = BSDFSample(sample, *bsdfSampleOffset, 0);
    }
    else {
        lightSample = LightSample(rng);
        bsdfSample = BSDFSample(rng);
    }
    return (float)nLights *
        EstimateDirect(scene, renderer, arena, light, p, n, wo,
                       rayEpsilon, time, bsdf, rng, lightSample,
                       bsdfSample, BxDFType(BSDF_ALL & ~BSDF_SPECULAR));
}
示例#7
0
static inline void mutate(RNG &rng, float *v, float min = 0.f, float max = 1.f) {
    if (min == max) { *v = min; return; }
    Assert(min < max);
    float s1 = 1.f / 1024.f, s2 = 1.f / 64.f;
    static const float negLogRatio = -logf(s2/s1);
    float delta = (max - min) * s2 * expf(negLogRatio * rng.RandomFloat());
    if (rng.RandomFloat() < 0.5f) {
        *v += delta;
        if (*v > max) *v = min + (*v - max);
    }
    else {
        *v -= delta;
        if (*v < min) *v = max - (min - *v);
    }
    Assert(*v >= min && *v <= max);
}
示例#8
0
Spectrum EmissionIntegrator::Li(const Scene *scene,
                                const Renderer *renderer, const RayDifferential &ray,
                                const Sample *sample, RNG &rng, Spectrum *T,
                                MemoryArena &arena) const {
    VolumeRegion *vr = scene->volumeRegion;
    Assert(sample != NULL);
    float t0, t1;
    if (!vr || !vr->IntersectP(ray, &t0, &t1) || (t1-t0) == 0.f) {
        *T = Spectrum(1.f);
        return 0.f;
    }
    // Do emission-only volume integration in _vr_
    Spectrum Lv(0.);

    // Prepare for volume integration stepping
    int nSamples = Ceil2Int((t1-t0) / stepSize);
    float step = (t1 - t0) / nSamples;
    Spectrum Tr(1.f);
    pbrt::Point p = ray(t0), pPrev;
    Vector w = -ray.d;
    t0 += sample->oneD[scatterSampleOffset][0] * step;
    for (int i = 0; i < nSamples; ++i, t0 += step) {
        // Advance to sample at _t0_ and update _T_
        pPrev = p;
        p = ray(t0);
        Ray tauRay(pPrev, p - pPrev, 0.f, 1.f, ray.time, ray.depth);
        Spectrum stepTau = vr->tau(tauRay,
                                   .5f * stepSize, rng.RandomFloat());
        Tr *= Exp(-stepTau);

        // Possibly terminate ray marching if transmittance is small
        if (Tr.y() < 1e-3) {
            const float continueProb = .5f;
            if (rng.RandomFloat() > continueProb) {
                Tr = 0.f;
                break;
            }
            Tr /= continueProb;
        }

        // Compute emission-only source term at _p_
        Lv += Tr * vr->Lve(p, w, ray.time);
    }
    *T = Tr;
    return Lv * step;
}
示例#9
0
static inline void mutate(RNG &rng, float *v, float min = 0.f,
                          float max = 1.f) {
    if (min == max) { *v = min; return; }
    Assert(min < max);
    float a = 1.f / 1024.f, b = 1.f / 64.f;
    static const float logRatio = -logf(b/a);
    float delta = (max - min) * b * expf(logRatio * rng.RandomFloat());
    if (rng.RandomFloat() < 0.5f) {
        *v += delta;
        if (*v >= max) *v = min + (*v - max);
    }
    else {
        *v -= delta;
        if (*v < min) *v = max - (min - *v);
    }
    if (*v < min || *v >= max) *v = min;
}
示例#10
0
// Monte Carlo Function Definitions
void RejectionSampleDisk(float *x, float *y, RNG &rng) {
    float sx, sy;
    do {
        sx = 1.f - 2.f * rng.RandomFloat();
        sy = 1.f - 2.f * rng.RandomFloat();
    } while (sx*sx + sy*sy > 1.f);
    *x = sx;
    *y = sy;
}
示例#11
0
void StratifiedSample2D(float *samp, int nx, int ny, RNG &rng, bool jitter) {
    float dx = 1.f / nx, dy = 1.f / ny;
    for (int y = 0; y < ny; ++y)
        for (int x = 0; x < nx; ++x) {
            float jx = jitter ? rng.RandomFloat() : 0.5f;
            float jy = jitter ? rng.RandomFloat() : 0.5f;
            *samp++ = (x + jx) * dx;
            *samp++ = (y + jy) * dy;
        }
}
示例#12
0
Spectrum VSDScatteringIntegrator::LiSingle(const Scene *scene, const Renderer *renderer,
        const RayDifferential &ray, const Sample *sample, RNG &rng,
        Spectrum *T, MemoryArena &arena) const {
    VolumeRegion *vr = scene->volumeRegion;
    float t0, t1;
    vr->IntersectP(ray, &t0, &t1);

    // Do single scattering volume integration in _vr_
    Spectrum Lv(0.);

    // Prepare for volume integration stepping
    int nSamples = Ceil2Int((t1-t0) / stepSize);
    float step = (t1 - t0) / nSamples;
    Spectrum Tr(1.f);
    Point p = ray(t0), pPrev;
    t0 += sample->oneD[scatterSampleOffset][0] * step;

    // Compute the emission from the voxels, not from the light sources
    for (int i = 0; i < nSamples; ++i, t0 += step) {
        // Advance to sample at _t0_ and update _T_
        pPrev = p;
        p = ray(t0);
        Ray tauRay(pPrev, p - pPrev, 0.f, 1.f, ray.time, ray.depth);
        Spectrum stepTau = vr->tau(tauRay,
                                   .5f * stepSize, rng.RandomFloat());
        Tr *= Exp(-stepTau);

        // Possibly terminate ray marching if transmittance is small
        if (Tr.y() < 1e-3) {
            const float continueProb = .5f;
            if (rng.RandomFloat() > continueProb) {
                Tr = 0.f;
                break;
            }
            Tr /= continueProb;
        }

        // Compute emission term at _p_
        Lv += Tr * vr->PhotonDensity(p);
    }
    *T = Tr;
    return Lv * step;
}
示例#13
0
//both rays have to be normalized + the vrl.mint is beginning of the media and vrl.maxt is the end of interraction. h will contain the smallest distance between the vectors
float sampleVRL(const Ray &r,const Ray &vrl, RNG& rng, float &h){
    float uh; //closest point parameter on camera ray
    float vh; //closest point parameter on vrl
    h=ln2lnPP(r, vrl, uh, vh);
    float phi=acosf(Dot(r.d, vrl.d));
    float v0d= vrl.mint-vh;
    float v1d= vrl.maxt-vh;
    float rnd=rng.RandomFloat();
    return invSqrCDF(rnd, phi, h, v0d, v1d);
}
示例#14
0
void Gen_UniformHemisphere(BSDF* bsdf,
    const Vector & wo, Vector* wi, float* pdf, Spectrum* f)
{
    float u1 = rng.RandomFloat();
    float u2 = rng.RandomFloat();
    Vector wiL = UniformSampleHemisphere(u1, u2);
    *wi = bsdf->LocalToWorld(wiL);
    *pdf = UniformHemispherePdf();

    *f = bsdf->f(wo, *wi);
}
示例#15
0
void Gen_CosHemisphere(BSDF* bsdf,
    const Vector & wo, Vector* wi, float* pdf, Spectrum* f)
{
    float u1 = rng.RandomFloat();
    float u2 = rng.RandomFloat();
    Vector wiL = CosineSampleHemisphere(u1, u2);
    *wi = bsdf->LocalToWorld(wiL);
    float cosTheta = wiL.z;
    *pdf = CosineHemispherePdf(cosTheta, u2);

    *f = bsdf->f(wo, *wi);
}
示例#16
0
void AggregateTest::Render(const Scene *scene) {
    RNG rng;
    ProgressReporter prog(nIterations, "Aggregate Test");
    // Compute bounding box of region used to generate random rays
    BBox bbox = scene->WorldBound();
    bbox.Expand(bbox.pMax[bbox.MaximumExtent()] -
                bbox.pMin[bbox.MaximumExtent()]);
    Point lastHit;
    float lastEps = 0.f;
    for (int i = 0; i < nIterations; ++i) {
        // Choose random rays, _rayAccel_ and _rayAll_ for testing

        // Choose ray origin for testing accelerator
        Point org(Lerp(rng.RandomFloat(), bbox.pMin.x, bbox.pMax.x),
                  Lerp(rng.RandomFloat(), bbox.pMin.y, bbox.pMax.y),
                  Lerp(rng.RandomFloat(), bbox.pMin.z, bbox.pMax.z));
        if ((rng.RandomUInt() % 4) == 0) org = lastHit;

        // Choose ray direction for testing accelerator
        Vector dir = UniformSampleSphere(rng.RandomFloat(), rng.RandomFloat());
        if ((rng.RandomUInt() % 32) == 0) dir.x = dir.y = 0.f;
        else if ((rng.RandomUInt() % 32) == 0) dir.x = dir.z = 0.f;
        else if ((rng.RandomUInt() % 32) == 0) dir.y = dir.z = 0.f;

        // Choose ray epsilon for testing accelerator
        float eps = 0.f;
        if (rng.RandomFloat() < .25) eps = lastEps;
        else if (rng.RandomFloat() < .25) eps = 1e-3f;
        Ray rayAccel(org, dir, eps);
        Ray rayAll = rayAccel;

        // Compute intersections using accelerator and exhaustive testing
        Intersection isectAccel, isectAll;
        bool hitAccel = scene->Intersect(rayAccel, &isectAccel);
        bool hitAll = false;
        bool inconsistentBounds = false;
        for (u_int j = 0; j < primitives.size(); ++j) {
            if (bboxes[j].IntersectP(rayAll))
                hitAll |= primitives[j]->Intersect(rayAll, &isectAll);
            else if (primitives[j]->Intersect(rayAll, &isectAll))
                inconsistentBounds = true;
        }

        // Report any inconsistencies between intersections
        if (!inconsistentBounds &&
            ((hitAccel != hitAll) || (rayAccel.maxt != rayAll.maxt)))
            Warning("Disagreement: t accel %.16g [%a] t exhaustive %.16g [%a]\n"
                    "Ray: org [%a, %a, %a], dir [%a, %a, %a], mint = %a",
                    rayAccel.maxt, rayAll.maxt, rayAccel.maxt, rayAll.maxt,
                    rayAll.o.x, rayAll.o.y, rayAll.o.z,
                    rayAll.d.x, rayAll.d.y, rayAll.d.z, rayAll.mint);
        if (hitAll) {
            lastHit = rayAll(rayAll.maxt);
            lastEps = isectAll.RayEpsilon;
        }
        prog.Update();
    }
    prog.Done();
}
示例#17
0
int RandomSampler::GetMoreSamples(Sample *sample, RNG &rng) {
    if (samplePos == nSamples) {
        if (xPixelStart == xPixelEnd || yPixelStart == yPixelEnd)
            return 0;
        if (++xPos == xPixelEnd) {
            xPos = xPixelStart;
            ++yPos;
        }
        if (yPos == yPixelEnd)
            return 0;

        for (int i = 0; i < 5 * nSamples; ++i)
            imageSamples[i] = rng.RandomFloat();

        // Shift image samples to pixel coordinates
        for (int o = 0; o < 2 * nSamples; o += 2) {
            imageSamples[o]   += xPos;
            imageSamples[o+1] += yPos;
        }
        samplePos = 0;
    }
    // Return next \mono{RandomSampler} sample point
    sample->imageX = imageSamples[2*samplePos];
    sample->imageY = imageSamples[2*samplePos+1];
    sample->lensU = lensSamples[2*samplePos];
    sample->lensV = lensSamples[2*samplePos+1];
    sample->time = Lerp(timeSamples[samplePos], shutterOpen, shutterClose);
    // Generate stratified samples for integrators
    for (uint32_t i = 0; i < sample->n1D.size(); ++i)
        for (uint32_t j = 0; j < sample->n1D[i]; ++j)
            sample->oneD[i][j] = rng.RandomFloat();
    for (uint32_t i = 0; i < sample->n2D.size(); ++i)
        for (uint32_t j = 0; j < 2*sample->n2D[i]; ++j)
            sample->twoD[i][j] = rng.RandomFloat();
    ++samplePos;
    return 1;
}
示例#18
0
Spectrum IrradianceCacheIntegrator::pathL(Ray &r, const Scene *scene,
        const Renderer *renderer, RNG &rng, MemoryArena &arena) const {
    Spectrum L(0.f);
    Spectrum pathThroughput = 1.;
    RayDifferential ray(r);
    bool specularBounce = false;
    for (int pathLength = 0; ; ++pathLength) {
        // Find next vertex of path
        Intersection isect;
        if (!scene->Intersect(ray, &isect))
            break;
        if (pathLength == 0)
            r.maxt = ray.maxt;
        pathThroughput *= renderer->Transmittance(scene, ray, NULL, rng, arena);
        // Possibly add emitted light at path vertex
        if (specularBounce)
            L += pathThroughput * isect.Le(-ray.d);
        // Evaluate BSDF at hit point
        BSDF *bsdf = isect.GetBSDF(ray, arena);
        // Sample illumination from lights to find path contribution
        const Point &p = bsdf->dgShading.p;
        const Normal &n = bsdf->dgShading.nn;
        Vector wo = -ray.d;
        L += pathThroughput *
            UniformSampleOneLight(scene, renderer, arena, p, n, wo, isect.rayEpsilon,
                                  ray.time, bsdf, NULL, rng);
        if (pathLength+1 == maxIndirectDepth) break;
        // Sample BSDF to get new path direction
        // Get random numbers for sampling new direction, \mono{bs1}, \mono{bs2}, and \mono{bcs}
        Vector wi;
        float pdf;
        BxDFType flags;
        Spectrum f = bsdf->Sample_f(wo, &wi, BSDFSample(rng),
            &pdf, BSDF_ALL, &flags);
        if (f.IsBlack() || pdf == 0.)
            break;
        specularBounce = (flags & BSDF_SPECULAR) != 0;
        pathThroughput *= f * AbsDot(wi, n) / pdf;
        ray = RayDifferential(p, wi, ray, isect.rayEpsilon);
        // Possibly terminate the path
        if (pathLength > 2) {
            float rrProb = min(1.f, pathThroughput.y());
            if (rng.RandomFloat() > rrProb)
                break;
            pathThroughput /= rrProb;
        }
    }
    return L;
}
示例#19
0
void LatinHypercube(float *samples, u_int nSamples, u_int nDim, RNG &rng) {
    // Generate LHS samples along diagonal
    float delta = 1.f / nSamples;
    for (u_int i = 0; i < nSamples; ++i)
        for (u_int j = 0; j < nDim; ++j)
            samples[nDim * i + j] = (i + (rng.RandomFloat())) * delta;

    // Permute LHS samples in each dimension
    for (u_int i = 0; i < nDim; ++i) {
        for (u_int j = 0; j < nSamples; ++j) {
            u_int other = j + (rng.RandomUInt() % (nSamples - j));
            swap(samples[nDim * j + i], samples[nDim * other + i]);
        }
    }
}
示例#20
0
Spectrum SingleScatteringFluorescenceRWLIntegrator::Transmittance(const Scene *scene,
        const Renderer *renderer, const RayDifferential &ray,
        const Sample *sample, RNG &rng, MemoryArena &arena) const {
    if (!scene->volumeRegion) return Spectrum(1.f);
    float step, offset;
    if (sample) {
        step = stepSize;
        offset = sample->oneD[tauSampleOffset][0];
    }
    else {
        step = 4.f * stepSize;
        offset = rng.RandomFloat();
    }
    Spectrum tau = scene->volumeRegion->tau(ray, step, offset);
    return Exp(-tau);
}
示例#21
0
static void LargeStep(RNG &rng, MLTSample *sample, int maxDepth,
        int x0, int x1, int y0, int y1, float t0, float t1) {
    sample->cameraSample.imageX = Lerp(rng.RandomFloat(), x0, x1);
    sample->cameraSample.imageY = Lerp(rng.RandomFloat(), y0, y1);
    sample->cameraSample.time = Lerp(rng.RandomFloat(), t0, t1);
    sample->cameraSample.lensU = rng.RandomFloat();
    sample->cameraSample.lensV = rng.RandomFloat();
    for (int i = 0; i < maxDepth; ++i) {
        // Apply large step to $i$th _PathSample_
        PathSample &ps = sample->pathSamples[i];
        ps.bsdfComponent = rng.RandomFloat();
        ps.bsdfDir0 = rng.RandomFloat();
        ps.bsdfDir1 = rng.RandomFloat();
        ps.bsdfLightComponent = rng.RandomFloat();
        ps.bsdfLightDir0 = rng.RandomFloat();
        ps.bsdfLightDir1 = rng.RandomFloat();
        ps.lightNum0 = rng.RandomFloat();
        ps.lightNum1 = rng.RandomFloat();
        ps.lightDir0 = rng.RandomFloat();
        ps.lightDir1 = rng.RandomFloat();
    }
}
示例#22
0
Spectrum VolumePatIntegrator::UniformSampleLight(const Scene *scene,
        const Renderer *renderer, MemoryArena &arena, const Point &p,
        const Normal &n, const Vector &wo, float rayEpsilon, float time, RNG &rng) const {
    // Randomly choose a single light to sample, _light_
    int nLights = int(scene->lights.size());
    if (nLights == 0) return Spectrum(0.);
    int lightNum;
    lightNum = Floor2Int(rng.RandomFloat() * nLights);
    lightNum = min(lightNum, nLights-1);
    Light *light = scene->lights[lightNum];

    // Initialize light sample for single light sampling
    LightSample lightSample(rng);

    return (float) nLights * EstimateDirectLight(scene, renderer, arena,
            light, p, n, wo, rayEpsilon, time, rng, lightSample);
}
示例#23
0
bool HomogeneousVolumeDensity::SampleDistance(const Ray &ray, float *tDist,
        Point &Psample, float *pdf, RNG &rng, const int &wl) const {
    // Compute the sampling step
    Vector w = -ray.d;
    float t = -log(1 - rng.RandomFloat()) / Sigma_t(ray.o, w, ray.time, wl);
    *tDist = ray.mint + t;
    Psample = ray(t);

    if (!extent.Inside(Psample)) {
        return false;
    } else {
        // Compute the PDF that is associated with this sample
        float extenctionCoeff = Sigma_t(Psample, w, ray.time, wl);
        float samplePdf = extenctionCoeff * exp(-extenctionCoeff * t);
        *pdf = samplePdf;
        return true;
    }
}
示例#24
0
Spectrum SingleScatteringFluorescenceRWLIntegrator::Li(const Scene *scene,
        const Renderer *renderer, const RayDifferential &ray,
        const Sample *sample, RNG &rng, Spectrum *T, MemoryArena &arena) const {
    VolumeRegion *vr = scene->volumeRegion;
    float t0, t1;
    if (!vr || !vr->IntersectP(ray, &t0, &t1) || (t1-t0) == 0.f) {
        *T = 1.f;
        return 0.f;
    }
    // Do single scattering volume integration in _vr_
    Spectrum Lv(0.);

    // Prepare for volume integration stepping
    int nSamples = Ceil2Int((t1-t0) / stepSize);
    float step = (t1 - t0) / nSamples;
    Spectrum Tr(1.f);
    Point p = ray(t0), pPrev;
    Vector w = -ray.d;
    t0 += sample->oneD[scatterSampleOffset][0] * step;

    // Compute sample patterns for single scattering samples
    float *lightNum = arena.Alloc<float>(nSamples);
    LDShuffleScrambled1D(1, nSamples, lightNum, rng);
    float *lightComp = arena.Alloc<float>(nSamples);
    LDShuffleScrambled1D(1, nSamples, lightComp, rng);
    float *lightPos = arena.Alloc<float>(2*nSamples);
    LDShuffleScrambled2D(1, nSamples, lightPos, rng);
    uint32_t sampOffset = 0;
    for (int i = 0; i < nSamples; ++i, t0 += step) {
        // Advance to sample at _t0_ and update _T_
        pPrev = p;
        p = ray(t0);

        Ray tauRay(pPrev, p - pPrev, 0.f, 1.f, ray.time, ray.depth);
        Spectrum stepTau = vr->tau(tauRay, 0.5f * stepSize, rng.RandomFloat());
        Tr *= Exp(-stepTau);

        // Possibly terminate ray marching if transmittance is small
        if (Tr.y() < 1e-3) {
            const float continueProb = .5f;
            if (rng.RandomFloat() > continueProb) {
                Tr = 0.f;
                break;
            }
            Tr /= continueProb;
        }

        // Compute fluorescence emission
        Spectrum sigma = vr->Mu(p, w, ray.time);
        if (!sigma.IsBlack() && scene->lights.size() > 0) {
            int nLights = scene->lights.size();
            int ln = min(Floor2Int(lightNum[sampOffset] * nLights),
                         nLights-1);
            Light *light = scene->lights[ln];

            // Add contribution of _light_ due to the in-scattering at _p_
            float pdf;
            VisibilityTester vis;
            Vector wo;
            LightSample ls(lightComp[sampOffset], lightPos[2*sampOffset],
                           lightPos[2*sampOffset+1]);
            Spectrum L = light->Sample_L(p, 0.f, ls, ray.time, &wo, &pdf, &vis);
            if (!L.IsBlack() && pdf > 0.f && vis.Unoccluded(scene)) {
                Spectrum Ld = L * vis.Transmittance(scene, renderer, NULL, rng,
                                                    arena);
                int lambdaExcIndex = light->GetLaserWavelengthIndex();
                float Lpower = Ld.GetLaserEmissionPower(lambdaExcIndex);
                float yield = vr->Yeild(Point());
                Spectrum fEx = vr->fEx(Point());
                Spectrum fEm = vr->fEm(Point());
                float scale = fEx.GetSampleValueAtWavelengthIndex(lambdaExcIndex);
                Lv += Lpower * Tr * sigma * vr->p(p, w, -wo, ray.time) *
                        scale * fEm * yield * float(nLights) / pdf;
            }
        }
        ++sampOffset;
    }
    *T = Tr;
    return Lv * step;
}
示例#25
0
void IGIIntegrator::Preprocess(const Scene *scene, const Camera *camera,
                               const Renderer *renderer) {
    if (scene->lights.size() == 0) return;
    MemoryArena arena;
    RNG rng;
    // Compute samples for emitted rays from lights
    vector<float> lightNum(nLightPaths * nLightSets);
    vector<float> lightSampPos(2 * nLightPaths * nLightSets, 0.f);
    vector<float> lightSampComp(nLightPaths * nLightSets, 0.f);
    vector<float> lightSampDir(2 * nLightPaths * nLightSets, 0.f);
    LDShuffleScrambled1D(nLightPaths, nLightSets, &lightNum[0], rng);
    LDShuffleScrambled2D(nLightPaths, nLightSets, &lightSampPos[0], rng);
    LDShuffleScrambled1D(nLightPaths, nLightSets, &lightSampComp[0], rng);
    LDShuffleScrambled2D(nLightPaths, nLightSets, &lightSampDir[0], rng);

    // Precompute information for light sampling densities
    Distribution1D *lightDistribution = ComputeLightSamplingCDF(scene);
    for (uint32_t s = 0; s < nLightSets; ++s) {
        for (uint32_t i = 0; i < nLightPaths; ++i) {
            // Follow path _i_ from light to create virtual lights
            int sampOffset = s*nLightPaths + i;

            // Choose light source to trace virtual light path from
            float lightPdf;
            int ln = lightDistribution->SampleDiscrete(lightNum[sampOffset],
                                                       &lightPdf);
            Light *light = scene->lights[ln];

            // Sample ray leaving light source for virtual light path
            RayDifferential ray;
            float pdf;
            LightSample ls(lightSampPos[2*sampOffset], lightSampPos[2*sampOffset+1],
                           lightSampComp[sampOffset]);
            Normal Nl;
            Spectrum alpha = light->Sample_L(scene, ls, lightSampDir[2*sampOffset],
                                             lightSampDir[2*sampOffset+1],
                                             camera->shutterOpen, &ray, &Nl, &pdf);
            if (pdf == 0.f || alpha.IsBlack()) continue;
            alpha /= pdf * lightPdf;
            Intersection isect;
            while (scene->Intersect(ray, &isect) && !alpha.IsBlack()) {
                // Create virtual light and sample new ray for path
                alpha *= renderer->Transmittance(scene, RayDifferential(ray), NULL,
                                                 rng, arena);
                Vector wo = -ray.d;
                BSDF *bsdf = isect.GetBSDF(ray, arena);

                // Create virtual light at ray intersection point
                Spectrum contrib = alpha * bsdf->rho(wo, rng) / M_PI;
                virtualLights[s].push_back(VirtualLight(isect.dg.p, isect.dg.nn, contrib,
                                                        isect.rayEpsilon));

                // Sample new ray direction and update weight for virtual light path
                Vector wi;
                float pdf;
                BSDFSample bsdfSample(rng);
                Spectrum fr = bsdf->Sample_f(wo, &wi, bsdfSample, &pdf);
                if (fr.IsBlack() || pdf == 0.f)
                    break;
                Spectrum contribScale = fr * AbsDot(wi, bsdf->dgShading.nn) / pdf;

                // Possibly terminate virtual light path with Russian roulette
                float rrProb = min(1.f, contribScale.y());
                if (rng.RandomFloat() > rrProb)
                    break;
                alpha *= contribScale / rrProb;
                ray = RayDifferential(isect.dg.p, wi, ray, isect.rayEpsilon);
            }
            arena.FreeAll();
        }
    }
    delete lightDistribution;
}
示例#26
0
Spectrum IGIIntegrator::Li(const Scene *scene, const Renderer *renderer,
        const RayDifferential &ray, const Intersection &isect,
        const Sample *sample, RNG &rng, MemoryArena &arena) const {
    Spectrum L(0.);
    Vector wo = -ray.d;
    // Compute emitted light if ray hit an area light source
    L += isect.Le(wo);

    // Evaluate BSDF at hit point
    BSDF *bsdf = isect.GetBSDF(ray, arena);
    const Point &p = bsdf->dgShading.p;
    const Normal &n = bsdf->dgShading.nn;
    L += UniformSampleAllLights(scene, renderer, arena, p, n,
                    wo, isect.rayEpsilon, ray.time, bsdf, sample, rng,
                    lightSampleOffsets, bsdfSampleOffsets);
    // Compute indirect illumination with virtual lights
    uint32_t lSet = min(uint32_t(sample->oneD[vlSetOffset][0] * nLightSets),
                        nLightSets-1);
    for (uint32_t i = 0; i < virtualLights[lSet].size(); ++i) {
        const VirtualLight &vl = virtualLights[lSet][i];
        // Compute virtual light's tentative contribution _Llight_
        float d2 = DistanceSquared(p, vl.p);
        Vector wi = Normalize(vl.p - p);
        float G = AbsDot(wi, n) * AbsDot(wi, vl.n) / d2;
        G = min(G, gLimit);
        Spectrum f = bsdf->f(wo, wi);
        if (G == 0.f || f.IsBlack()) continue;
        Spectrum Llight = f * G * vl.pathContrib / nLightPaths;
        RayDifferential connectRay(p, wi, ray, isect.rayEpsilon,
                                   sqrtf(d2) * (1.f - vl.rayEpsilon));
        Llight *= renderer->Transmittance(scene, connectRay, NULL, rng, arena);

        // Possibly skip virtual light shadow ray with Russian roulette
        if (Llight.y() < rrThreshold) {
            float continueProbability = .1f;
            if (rng.RandomFloat() > continueProbability)
                continue;
            Llight /= continueProbability;
        }

        // Add contribution from _VirtualLight_ _vl_
        if (!scene->IntersectP(connectRay))
            L += Llight;
    }
    if (ray.depth < maxSpecularDepth) {
        // Do bias compensation for bounding geometry term
        int nSamples = (ray.depth == 0) ? nGatherSamples : 1;
        for (int i = 0; i < nSamples; ++i) {
            Vector wi;
            float pdf;
            BSDFSample bsdfSample = (ray.depth == 0) ?
                BSDFSample(sample, gatherSampleOffset, i) : BSDFSample(rng);
            Spectrum f = bsdf->Sample_f(wo, &wi, bsdfSample,
                                        &pdf, BxDFType(BSDF_ALL & ~BSDF_SPECULAR));
            if (!f.IsBlack() && pdf > 0.f) {
                // Trace ray for bias compensation gather sample
                float maxDist = sqrtf(AbsDot(wi, n) / gLimit);
                RayDifferential gatherRay(p, wi, ray, isect.rayEpsilon, maxDist);
                Intersection gatherIsect;
                Spectrum Li = renderer->Li(scene, gatherRay, sample, rng, arena,
                                           &gatherIsect);
                if (Li.IsBlack()) continue;

                // Add bias compensation ray contribution to radiance sum
                float Ggather = AbsDot(wi, n) * AbsDot(-wi, gatherIsect.dg.nn) /
                    DistanceSquared(p, gatherIsect.dg.p);
                if (Ggather - gLimit > 0.f && !isinf(Ggather)) {
                    float gs = (Ggather - gLimit) / Ggather;
                    L += f * Li * (AbsDot(wi, n) * gs / (nSamples * pdf));
                }
            }
        }
    }
    if (ray.depth + 1 < maxSpecularDepth) {
        Vector wi;
        // Trace rays for specular reflection and refraction
        L += SpecularReflect(ray, bsdf, rng, isect, renderer, scene, sample,
                             arena);
        L += SpecularTransmit(ray, bsdf, rng, isect, renderer, scene, sample,
                              arena);
    }
    return L;
}
示例#27
0
文件: path.cpp 项目: gmlealll/pbrt-v2
Spectrum PathIntegrator::Li(const Scene *scene, const Renderer *renderer,
        const RayDifferential &r, const Intersection &isect,
        const Sample *sample, RNG &rng, MemoryArena &arena) const {
    // Declare common path integration variables
    Spectrum pathThroughput = 1., L = 0.;
    RayDifferential ray(r);
    bool specularBounce = false;
    Intersection localIsect;
    const Intersection *isectp = &isect;
    for (int bounces = 0; ; ++bounces) {
        // Possibly add emitted light at path vertex
        if (bounces == 0 || specularBounce)
            L += pathThroughput * isectp->Le(-ray.d);

        // Sample illumination from lights to find path contribution
        BSDF *bsdf = isectp->GetBSDF(ray, arena);
        const Point &p = bsdf->dgShading.p;
        const Normal &n = bsdf->dgShading.nn;
        Vector wo = -ray.d;
        if (bounces < SAMPLE_DEPTH)
            L += pathThroughput *
                 UniformSampleOneLight(scene, renderer, arena, p, n, wo,
                     isectp->rayEpsilon, ray.time, bsdf, sample, rng,
                     lightNumOffset[bounces], &lightSampleOffsets[bounces],
                     &bsdfSampleOffsets[bounces]);
        else
            L += pathThroughput *
                 UniformSampleOneLight(scene, renderer, arena, p, n, wo,
                     isectp->rayEpsilon, ray.time, bsdf, sample, rng);

        // Sample BSDF to get new path direction

        // Get _outgoingBSDFSample_ for sampling new path direction
        BSDFSample outgoingBSDFSample;
        if (bounces < SAMPLE_DEPTH)
            outgoingBSDFSample = BSDFSample(sample, pathSampleOffsets[bounces], 0);
        else
            outgoingBSDFSample = BSDFSample(rng);
        Vector wi;
        float pdf;
        BxDFType flags;
        Spectrum f = bsdf->Sample_f(wo, &wi, outgoingBSDFSample, &pdf,
                                    BSDF_ALL, &flags);
        if (f.IsBlack() || pdf == 0.)
            break;
        specularBounce = (flags & BSDF_SPECULAR) != 0;
        pathThroughput *= f * AbsDot(wi, n) / pdf;
        ray = RayDifferential(p, wi, ray, isectp->rayEpsilon);

        // Possibly terminate the path
        if (bounces > 3) {
            float continueProbability = min(.5f, pathThroughput.y());
            if (rng.RandomFloat() > continueProbability)
                break;
            pathThroughput /= continueProbability;
        }
        if (bounces == maxDepth)
            break;

        // Find next vertex of path
        if (!scene->Intersect(ray, &localIsect)) {
            if (specularBounce)
                for (uint32_t i = 0; i < scene->lights.size(); ++i)
                   L += pathThroughput * scene->lights[i]->Le(ray);
            break;
        }
        if (bounces > 1)
            pathThroughput *= renderer->Transmittance(scene, ray, NULL, rng, arena);
        isectp = &localIsect;
    }
    return L;
}
示例#28
0
Spectrum PhotonVolumeIntegrator::Li(const Scene *scene, const Renderer *renderer,
        const RayDifferential &ray, const Sample *sample, RNG &rng,
        Spectrum *T, MemoryArena &arena) const {
 	
 	VolumeRegion *vr = scene->volumeRegion;
    RainbowVolume* rv = dynamic_cast<RainbowVolume*>(vr);
 	KdTree<Photon>* volumeMap = photonShooter->volumeMap; 

 	float t0, t1;
 	if (!vr || !vr->IntersectP(ray, &t0, &t1) || (t1-t0) == 0.f){
 		*T = 1.f;
 	 	return 0.f;
 	 }
 	// Do single scattering & photon multiple scattering volume integration in _vr_
 	Spectrum Lv(0.);


 	// Prepare for volume integration stepping
 	int nSamples = Ceil2Int((t1-t0) / stepSize);
 	float step = (t1 - t0) / nSamples;
 	Spectrum Tr(1.f);
 	Point p = ray(t0), pPrev;
 	Vector w = -ray.d;
 	t0 += sample->oneD[scatterSampleOffset][0] * step;

 	float *lightNum = arena.Alloc<float>(nSamples);
    LDShuffleScrambled1D(1, nSamples, lightNum, rng);
    float *lightComp = arena.Alloc<float>(nSamples);
    LDShuffleScrambled1D(1, nSamples, lightComp, rng);
    float *lightPos = arena.Alloc<float>(2*nSamples);
    LDShuffleScrambled2D(1, nSamples, lightPos, rng);
 	int sampOffset = 0;

 	ClosePhoton *lookupBuf = new ClosePhoton[nSamples];

 	for (int i = 0; i < nSamples; ++i, t0 += step) {
 		// Advance to sample at _t0_ and update _T_
 		pPrev = p;
 		p = ray(t0);

 		Ray tauRay(pPrev, p - pPrev, 0.f, 1.f, ray.time, ray.depth);

 		Spectrum stepTau = vr->tau(tauRay,.5f * stepSize, rng.RandomFloat());
 		Tr = Exp(-stepTau);

 		// Possibly terminate raymarching if transmittance is small.
 		if (Tr.y() < 1e-3) {
 			const float continueProb = .5f;
 			if (rng.RandomFloat() > continueProb){
 				Tr = 0.f;
 				break;
 			}
 			Tr /= continueProb;
 		}
		
		
 		// Compute single-scattering source term at _p_ & photon mapped MS
 		Spectrum L_i(0.);
 		Spectrum L_d(0.);
 		Spectrum L_ii(0.);
 		
 		// Lv += Tr*vr->Lve(p, w, ray.time);
 		Spectrum ss = vr->sigma_s(p, w, ray.time);
 		Spectrum sa = vr->sigma_a(p, w, ray.time);

 		if (!ss.IsBlack() && scene->lights.size() > 0) {
 			int nLights = scene->lights.size();
 			int ln =
 				min(Floor2Int(lightNum[sampOffset] * nLights),
 				    nLights-1);
 			Light *light = scene->lights[ln];
 			// Add contribution of _light_ due to scattering at _p_
 			float pdf;
 			VisibilityTester vis;
 			Vector wo;

 			LightSample ls(lightComp[sampOffset], lightPos[2*sampOffset],
                           lightPos[2*sampOffset+1]);
            Spectrum L = light->Sample_L(p, 0.f, ls, ray.time, &wo, &pdf, &vis);
            

 			if (!L.IsBlack() && pdf > 0.f && vis.Unoccluded(scene)) {

                Spectrum Ld = L * vis.Transmittance(scene,renderer, NULL, rng, arena);
                if(rv){
                    L_d = rv->rainbowReflection(Ld, ray.d, wo);
                }
                else {
                    L_d = vr->p(p, w, -wo, ray.time) * Ld * float(nLights)/pdf;
                }
 			}
 		}
		// Compute 'indirect' in-scattered radiance from photon map
        if(!rv){
            L_ii += LPhoton(volumeMap, nUsed, lookupBuf, w, p, vr, maxDistSquared, ray.time);
        }
        
		// Compute total in-scattered radiance
		if (sa.y()!=0.0 || ss.y()!=0.0)
			L_i = L_d + (ss/(sa+ss))*L_ii;
		else
			L_i = L_d;

		Spectrum nLv = (sa*vr->Lve(p,w,ray.time)*step) + (ss*L_i*step) + (Tr * Lv)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            ;

		Lv = nLv;
 		sampOffset++;
 	}
 	*T = Tr;
	return Lv;
}
void CreateRadianceProbes::Render(const Scene *scene) {
    // Compute scene bounds and initialize probe integrators
    if (bbox.pMin.x > bbox.pMax.x)
        bbox = scene->WorldBound();
    surfaceIntegrator->Preprocess(scene, camera, this);
    volumeIntegrator->Preprocess(scene, camera, this);
    Sample *origSample = new Sample(NULL, surfaceIntegrator, volumeIntegrator,
                                    scene);

    // Compute sampling rate in each dimension
    Vector delta = bbox.pMax - bbox.pMin;
    int nProbes[3];
    for (int i = 0; i < 3; ++i)
        nProbes[i] = max(1, Ceil2Int(delta[i] / probeSpacing));

    // Allocate SH coefficient vector pointers for sample points
    int count = nProbes[0] * nProbes[1] * nProbes[2];
    Spectrum **c_in = new Spectrum *[count];
    for (int i = 0; i < count; ++i)
        c_in[i] = new Spectrum[SHTerms(lmax)];

    // Compute random points on surfaces of scene

    // Create scene bounding sphere to catch rays that leave the scene
    Point sceneCenter;
    float sceneRadius;
    scene->WorldBound().BoundingSphere(&sceneCenter, &sceneRadius);
    Transform ObjectToWorld(Translate(sceneCenter - Point(0,0,0)));
    Transform WorldToObject(Inverse(ObjectToWorld));
    Reference<Shape> sph = new Sphere(&ObjectToWorld, &WorldToObject,
        true, sceneRadius, -sceneRadius, sceneRadius, 360.f);
    Reference<Material> nullMaterial = Reference<Material>(NULL);
    GeometricPrimitive sphere(sph, nullMaterial, NULL);
    vector<Point> surfacePoints;
    uint32_t nPoints = 32768, maxDepth = 32;
    surfacePoints.reserve(nPoints + maxDepth);
    Point pCamera = camera->CameraToWorld(camera->shutterOpen,
                                          Point(0, 0, 0));
    surfacePoints.push_back(pCamera);
    RNG rng;
    while (surfacePoints.size() < nPoints) {
        // Generate random path from camera and deposit surface points
        Point pray = pCamera;
        Vector dir = UniformSampleSphere(rng.RandomFloat(), rng.RandomFloat());
        float rayEpsilon = 0.f;
        for (uint32_t i = 0; i < maxDepth; ++i) {
            Ray ray(pray, dir, rayEpsilon, INFINITY, time);

            Intersection isect;
            if (!scene->Intersect(ray, &isect) &&
                !sphere.Intersect(ray, &isect))
                break;

            surfacePoints.push_back(ray(ray.maxt));

            DifferentialGeometry &hitGeometry = isect.dg;
            pray = isect.dg.p;
            rayEpsilon = isect.rayEpsilon;
            hitGeometry.nn = Faceforward(hitGeometry.nn, -ray.d);

            dir = UniformSampleSphere(rng.RandomFloat(), rng.RandomFloat());
            dir = Faceforward(dir, hitGeometry.nn);
        }
    }

    // Launch tasks to compute radiance probes at sample points
    vector<Task *> tasks;
    ProgressReporter prog(count, "Radiance Probes");
    for (int i = 0; i < count; ++i)
        tasks.push_back(new CreateRadProbeTask(i, nProbes, time,
                                   bbox, lmax, includeDirectInProbes,
                                   includeIndirectInProbes, nIndirSamples,
                                   prog, origSample, surfacePoints,
                                   scene, this, c_in[i]));
    EnqueueTasks(tasks);
    WaitForAllTasks();
    for (uint32_t i = 0; i < tasks.size(); ++i)
        delete tasks[i];
    prog.Done();

    // Write radiance probe coefficients to file
    FILE *f = fopen(filename.c_str(), "w");
    if (f) {
        if (fprintf(f, "%d %d %d\n", lmax, includeDirectInProbes?1:0, includeIndirectInProbes?1:0) < 0 ||
            fprintf(f, "%d %d %d\n", nProbes[0], nProbes[1], nProbes[2]) < 0 ||
            fprintf(f, "%f %f %f %f %f %f\n", bbox.pMin.x, bbox.pMin.y, bbox.pMin.z,
                    bbox.pMax.x, bbox.pMax.y, bbox.pMax.z) < 0) {
            Error("Error writing radiance file \"%s\" (%s)", filename.c_str(),
                  strerror(errno));
            exit(1);
        }

        for (int i = 0; i < nProbes[0] * nProbes[1] * nProbes[2]; ++i) {
            for (int j = 0; j < SHTerms(lmax); ++j) {
                fprintf(f, "  ");
                if (c_in[i][j].Write(f) == false) {
                    Error("Error writing radiance file \"%s\" (%s)", filename.c_str(),
                          strerror(errno));
                    exit(1);
                }
                fprintf(f, "\n");
            }
            fprintf(f, "\n");
        }
        fclose(f);
    }
    for (int i = 0; i < nProbes[0] * nProbes[1] * nProbes[2]; ++i)
        delete[] c_in[i];
    delete[] c_in;
    delete origSample;
}
示例#30
0
static void LargeStep(RNG &rng, MLTSample *sample, int maxDepth,
        float x, float y, float t0, float t1, bool bidirectional) {
    // Do large step mutation of _cameraSample_
    sample->cameraSample.imageX = x;
    sample->cameraSample.imageY = y;
    sample->cameraSample.time = Lerp(rng.RandomFloat(), t0, t1);
    sample->cameraSample.lensU = rng.RandomFloat();
    sample->cameraSample.lensV = rng.RandomFloat();
    for (int i = 0; i < maxDepth; ++i) {
        // Apply large step to $i$th camera _PathSample_
        PathSample &cps = sample->cameraPathSamples[i];
        cps.bsdfSample.uComponent = rng.RandomFloat();
        cps.bsdfSample.uDir[0] = rng.RandomFloat();
        cps.bsdfSample.uDir[1] = rng.RandomFloat();
        cps.rrSample = rng.RandomFloat();

        // Apply large step to $i$th _LightingSample_
        LightingSample &ls = sample->lightingSamples[i];
        ls.bsdfSample.uComponent = rng.RandomFloat();
        ls.bsdfSample.uDir[0] = rng.RandomFloat();
        ls.bsdfSample.uDir[1] = rng.RandomFloat();
        ls.lightNum = rng.RandomFloat();
        ls.lightSample.uComponent = rng.RandomFloat();
        ls.lightSample.uPos[0] = rng.RandomFloat();
        ls.lightSample.uPos[1] = rng.RandomFloat();
    }
    if (bidirectional) {
        // Apply large step to bidirectional light samples
        sample->lightNumSample = rng.RandomFloat();
        for (int i = 0; i < 5; ++i)
            sample->lightRaySamples[i] = rng.RandomFloat();
        for (int i = 0; i < maxDepth; ++i) {
            // Apply large step to $i$th light _PathSample_
            PathSample &lps = sample->lightPathSamples[i];
            lps.bsdfSample.uComponent = rng.RandomFloat();
            lps.bsdfSample.uDir[0] = rng.RandomFloat();
            lps.bsdfSample.uDir[1] = rng.RandomFloat();
            lps.rrSample = rng.RandomFloat();
        }
    }
}