bool MultiQuadLight::sampleDirect(uint32 threadIndex, const Vec3f &p, PathSampleGenerator &pathSampler, LightSample &sample) const { float xi = pathSampler.next1D(EmitterSample); ThreadlocalSampleInfo &sampler = *_samplers[threadIndex]; std::pair<int, float> result = _sampleBvh->sampleLight(p, &sampler.sampleWeights[0], &sampler.insideIds[0], xi, [&](uint32 id) { return approximateQuadContribution(p, id); }); if (result.first == -1) return false; int idx = pathSampler.next1D(EmitterSample) < 0.5f ? result.first*2 : result.first*2 + 1; const QuadGeometry::TriangleInfo &t = _geometry.triangle(idx); Vec3f q = SampleWarp::uniformTriangle(pathSampler.next2D(EmitterSample), t.p0, t.p1, t.p2); Vec3f L = q - p; float rSq = L.lengthSq(); sample.dist = std::sqrt(rSq); sample.d = L/sample.dist; float cosTheta = -(t.Ng.dot(sample.d)); if (cosTheta <= 0.0f) return false; sample.pdf = result.second*0.5f*rSq/(cosTheta*_triangleAreas[idx]); return true; }
bool Cube::samplePosition(PathSampleGenerator &sampler, PositionSample &sample) const { float u = sampler.next1D(); int dim = sampleFace(u); float s = (dim + 1) % 3; float t = (dim + 2) % 3; Vec2f xi = sampler.next2D(); Vec3f n(0.0f); n[dim] = u < 0.5f ? -1.0f : 1.0f; Vec3f p(0.0f); p[dim] = n[dim]*_scale[dim]; p[s] = (xi.x()*2.0f - 1.0f)*_scale[s]; p[t] = (xi.y()*2.0f - 1.0f)*_scale[t]; sample.p = _rot*p + _pos; sample.pdf = _invArea; sample.uv = xi; sample.weight = PI*_area*(*_emission)[sample.uv]; sample.Ng = _rot*n; return true; }
float ErlangTransmittance::sampleSurface(PathSampleGenerator &sampler) const { float xi = sampler.next1D(); float x = 0.5f; for (int i = 0; i < 10; ++i) { x += (xi - (1.0f - surfaceSurface(Vec3f(x))[0]))/surfaceMedium(Vec3f(x))[0]; x = max(x, 0.0f); } return x; }
bool ExponentialMedium::sampleDistance(PathSampleGenerator &sampler, const Ray &ray, MediumState &state, MediumSample &sample) const { if (state.bounce > _maxBounce) return false; float x = _falloffScale*(ray.pos() - _unitPoint).dot(_unitFalloffDirection); float dx = _falloffScale*ray.dir().dot(_unitFalloffDirection); float maxT = ray.farT(); if (_absorptionOnly) { if (maxT == Ray::infinity() && dx <= 0.0f) return false; sample.t = maxT; sample.weight = std::exp(-_sigmaT*densityIntegral(x, dx, ray.farT())); sample.pdf = 1.0f; sample.exited = true; } else { int component = sampler.nextDiscrete(3); float sigmaTc = _sigmaT[component]; float xi = 1.0f - sampler.next1D(); float logXi = std::log(xi); float t = inverseOpticalDepth(x, dx, sigmaTc, logXi); sample.t = min(t, maxT); sample.weight = std::exp(-_sigmaT*densityIntegral(x, dx, sample.t)); sample.exited = (t >= maxT); if (sample.exited) { sample.pdf = sample.weight.avg(); } else { float rho = density(x, dx, sample.t); sample.pdf = (rho*_sigmaT*sample.weight).avg(); sample.weight *= rho*_sigmaS; } sample.weight /= sample.pdf; state.advance(); } sample.p = ray.pos() + sample.t*ray.dir(); sample.phase = _phaseFunction.get(); return true; }
bool AtmosphericMedium::sampleDistance(PathSampleGenerator &sampler, const Ray &ray, MediumState &state, MediumSample &sample) const { if (state.bounce > _maxBounce) return false; Vec3f p = (ray.pos() - _center); float t0 = p.dot(ray.dir()); float h = (p - t0*ray.dir()).length(); float maxT = ray.farT() + t0; if (_absorptionOnly) { sample.t = ray.farT(); sample.weight = std::exp(-_sigmaT*densityIntegral(h, t0, maxT)); sample.pdf = 1.0f; sample.exited = true; } else { int component = sampler.nextDiscrete(3); float sigmaTc = _sigmaT[component]; float xi = 1.0f - sampler.next1D(); float t = inverseOpticalDepth(h, t0, sigmaTc, xi); sample.t = min(t, maxT); sample.weight = std::exp(-_sigmaT*densityIntegral(h, t0, sample.t)); sample.exited = (t >= maxT); if (sample.exited) { sample.pdf = sample.weight.avg(); } else { float rho = density(h, sample.t); sample.pdf = (rho*_sigmaT*sample.weight).avg(); sample.weight *= rho*_sigmaS; } sample.weight /= sample.pdf; sample.t -= t0; state.advance(); } sample.p = ray.pos() + sample.t*ray.dir(); sample.phase = _phaseFunction.get(); return true; }
bool HomogeneousMedium::sampleDistance(PathSampleGenerator &sampler, const Ray &ray, MediumState &state, MediumSample &sample) const { if (state.bounce > _maxBounce) return false; float maxT = ray.farT(); if (_absorptionOnly) { if (maxT == Ray::infinity()) return false; sample.t = maxT; sample.weight = std::exp(-_sigmaT*maxT); sample.pdf = 1.0f; sample.exited = true; } else { int component = sampler.nextDiscrete(3); float sigmaTc = _sigmaT[component]; float t = -std::log(1.0f - sampler.next1D())/sigmaTc; sample.t = min(t, maxT); sample.weight = std::exp(-sample.t*_sigmaT); sample.exited = (t >= maxT); if (sample.exited) { sample.pdf = sample.weight.avg(); } else { sample.pdf = (_sigmaT*sample.weight).avg(); sample.weight *= _sigmaS; } sample.weight /= sample.pdf; state.advance(); } sample.p = ray.pos() + sample.t*ray.dir(); sample.phase = _phaseFunction.get(); return true; }
Vec2f VdbGrid::inverseOpticalDepth(PathSampleGenerator &sampler, Vec3f p, Vec3f w, float t0, float t1, float sigmaT, float xi) const { auto accessor = _grid->getConstAccessor(); if (_sampleMethod == SampleMethod::ExactNearest) { VdbRaymarcher<openvdb::FloatGrid::TreeType, 3> dda; float integral = 0.0f; Vec2f result(t1, 0.0f); bool exited = !dda.march(DdaRay(p + 0.5f, w), t0, t1, accessor, [&](openvdb::Coord voxel, float ta, float tb) { float v = accessor.getValue(voxel); float delta = v*sigmaT*(tb - ta); if (integral + delta >= xi) { result = Vec2f(ta + (tb - ta)*(xi - integral)/delta, v); return true; } integral += delta; return false; }); return exited ? Vec2f(t1, integral) : result; } else if (_sampleMethod == SampleMethod::ExactLinear) { VdbRaymarcher<openvdb::FloatGrid::TreeType, 3> dda; float integral = 0.0f; float fa = gridAt(accessor, p + w*t0); Vec2f result(t1, 0.0f); bool exited = !dda.march(DdaRay(p + 0.5f, w), t0, t1, accessor, [&](openvdb::Coord /*voxel*/, float ta, float tb) { float fb = gridAt(accessor, p + tb*w); float delta = (fb + fa)*0.5f*sigmaT*(tb - ta); if (integral + delta >= xi) { float a = (fb - fa)*sigmaT; float b = fa*sigmaT; float c = (integral - xi)/(tb - ta); float mantissa = max(b*b - 2.0f*a*c, 0.0f); float x1 = (-b + std::sqrt(mantissa))/a; result = Vec2f(ta + (tb - ta)*x1, fa + (fb - fa)*x1); return true; } integral += delta; fa = fb; return false; }); return exited ? Vec2f(t1, integral) : result; } else { float ta = t0; float fa = gridAt(accessor, p + w*t0); float integral = 0.0f; float dT = sampler.next1D()*_stepSize; do { float tb = min(ta + dT, t1); float fb = gridAt(accessor, p + w*tb); float delta = (fa + fb)*sigmaT*0.5f*(tb - ta); if (integral + delta >= xi) { float a = (fb - fa)*sigmaT; float b = fa*sigmaT; float c = (integral - xi)/(tb - ta); float mantissa = max(b*b - 2.0f*a*c, 0.0f); float x1 = (-b + std::sqrt(mantissa))/a; return Vec2f(ta + (tb - ta)*x1, fa + (fb - fa)*x1); } integral += delta; ta = tb; fa = fb; dT = _stepSize; } while (ta < t1); return Vec2f(t1, integral); } }
Vec3f VdbGrid::transmittance(PathSampleGenerator &sampler, Vec3f p, Vec3f w, float t0, float t1, Vec3f sigmaT) const { auto accessor = _grid->getConstAccessor(); if (_integrationMethod == IntegrationMethod::ExactNearest) { VdbRaymarcher<openvdb::FloatGrid::TreeType, 3> dda; float integral = 0.0f; dda.march(DdaRay(p + 0.5f, w), t0, t1, accessor, [&](openvdb::Coord voxel, float ta, float tb) { integral += accessor.getValue(voxel)*(tb - ta); return false; }); return std::exp(-integral*sigmaT); } else if (_integrationMethod == IntegrationMethod::ExactLinear) { VdbRaymarcher<openvdb::FloatGrid::TreeType, 3> dda; float integral = 0.0f; float fa = gridAt(accessor, p + w*t0); dda.march(DdaRay(p, w), t0, t1, accessor, [&](openvdb::Coord /*voxel*/, float ta, float tb) { float fb = gridAt(accessor, p + w*tb); integral += (fa + fb)*0.5f*(tb - ta); fa = fb; return false; }); return std::exp(-integral*sigmaT); } else if (_integrationMethod == IntegrationMethod::ResidualRatio) { VdbRaymarcher<Vec2fGrid::TreeType, 3> dda; float scale = _supergridSubsample; float invScale = 1.0f/scale; sigmaT *= scale; float sigmaTc = sigmaT.max(); auto superAccessor = _superGrid->getConstAccessor(); UniformSampler &generator = sampler.uniformGenerator(); float controlIntegral = 0.0f; Vec3f Tr(1.0f); dda.march(DdaRay(p*invScale + 0.5f, w), t0*invScale, t1*invScale, superAccessor, [&](openvdb::Coord voxel, float ta, float tb) { openvdb::Vec2s v = superAccessor.getValue(voxel); float muC = v.x(); float muR = v.y(); muR *= sigmaTc; controlIntegral += muC*(tb - ta); while (true) { ta -= BitManip::normalizedLog(generator.nextI())/muR; if (ta >= tb) break; Tr *= 1.0f - sigmaT*((gridAt(accessor, p + w*ta*scale) - muC)/muR); } return false; }); return std::exp(-controlIntegral*sigmaT)*Tr; } else { float ta = t0; float fa = gridAt(accessor, p + w*t0); float integral = 0.0f; float dT = sampler.next1D()*_stepSize; do { float tb = min(ta + dT, t1); float fb = gridAt(accessor, p + w*tb); integral += (fa + fb)*0.5f*(tb - ta); ta = tb; fa = fb; dT = _stepSize; } while (ta < t1); return std::exp(-integral*sigmaT); } }
float ErlangTransmittance::sampleMedium(PathSampleGenerator &sampler) const { return -1.0f/_lambda*std::log(sampler.next1D()*sampler.next1D()); }